Skip to content
Back to home

§ INTEGRATION · MODEL CONTEXT PROTOCOL

MCP client wrapper

One call wraps an MCP ClientSession (Python) or Client (JS). Every callTool runs through Execlave tool-allowlist enforcement before the MCP server is contacted.

§ 01

Prerequisites

  • Execlave account with an active API key — Settings → API keys.
  • A registered agent in Execlave. Use its lowercase-kebab agent_id.
  • MCP SDK: Python mcp >=1.0, or JS @modelcontextprotocol/sdk.
§ 02

Install

Instrumentation ships behind an optional extra (Python) and a subpath export (JS) so default installs do not pull the MCP SDK.

Python

pip install 'execlave-sdk[mcp]'

JavaScript / TypeScript

npm install @execlave/sdk @modelcontextprotocol/sdk
§ 03

Instant setup with a copilot

Paste this prompt into your AI coding assistant.

§ Copy prompt · paste into your AI coding assistant
You are adding Execlave (an AI agent governance platform) to an existing project that uses the Model Context Protocol (MCP) SDK. Do not rewrite the MCP server or transport — only attach instrumentation to the CLIENT session. Rules you MUST follow:1. Install the correct extra. Python: `pip install 'execlave-sdk[mcp]'`. JS/TS: `npm install @execlave/sdk @modelcontextprotocol/sdk`.2. Read `EXECLAVE_API_KEY` from environment variables. Never hardcode keys. Add it to `.env.example`.3. Create exactly ONE Execlave client per process and reuse it.4. Wrap the MCP client/session IMMEDIATELY AFTER construction, BEFORE the first `call_tool` / `callTool` invocation. Python: `instrument_mcp_session(session, exe, agent_id="<stable-kebab-id>")`. JS: `instrumentMcpClient(mcp, exe, { agentId: '<stable-kebab-id>' })`.5. `agent_id` is a stable kebab-case string identifying this MCP-using agent in the Execlave dashboard.6. The wrap is IDEMPOTENT — a `_execlave_instrumented` marker is set so calling it twice is a no-op. Do not add your own check.7. Wrap the `call_tool` / `callTool` invocation in try/except for `execlave.errors.PolicyBlockedError` (Python) or `PolicyBlockedError` from `@execlave/sdk` (JS). On block, return a structured 4xx response containing `exc.violations`. Do NOT swallow.8. Do NOT call `exe.enforce_policy` manually before `call_tool` — the wrapper runs tool-allowlist enforcement on the tool name and arguments.9. Both `call_tool(name, args)` and `call_tool({name, arguments})` shapes are handled by the wrapper — do not normalise the call site yourself.10. On process shutdown, call `exe.flush()` (sync) or `await exe.shutdown()` (JS). Deliverables:- One diff per file. Touch only the MCP client construction site and the call site for tool execution.- Add `EXECLAVE_API_KEY` and optional `EXECLAVE_BASE_URL` to `.env.example`. Reference: https://www.execlave.com/docs/integrations/mcpAPI reference: https://www.execlave.com/docs/sdk-reference
§ 04

Quick start

Wrap the MCP client once; every tool call is governed.

Python

from mcp import ClientSessionfrom execlave import Execlavefrom execlave.integrations.mcp import instrument_mcp_session exe = Execlave(api_key="exe_prod_...") session = ClientSession(...)  # your normal MCP setupinstrument_mcp_session(session, exe, agent_id="ops-agent") # Every call_tool now enforces tool-allowlist policies BEFORE the# request reaches the MCP server. A PolicyBlockedError aborts the# call and never contacts the server.result = await session.call_tool("filesystem.read", {"path": "/etc/passwd"})

JavaScript / TypeScript

import { Client } from '@modelcontextprotocol/sdk/client/index.js';import { Execlave } from '@execlave/sdk';import { instrumentMcpClient } from '@execlave/sdk/integrations/mcp'; const exe = new Execlave({ apiKey: process.env.EXECLAVE_API_KEY! }); const mcp = new Client({ name: 'ops-app', version: '1.0' }, { capabilities: {} });instrumentMcpClient(mcp, exe, { agentId: 'ops-agent' }); const result = await mcp.callTool({  name: 'filesystem.read',  arguments: { path: '/etc/passwd' },});
§ 05

Handling blocks

A blocked tool call raises PolicyBlockedError BEFORE the request reaches the MCP server, so blocked actions cannot leak side-effects.

Python

from execlave.errors import PolicyBlockedError try:    result = await session.call_tool(name, arguments)except PolicyBlockedError as exc:    return {"error": "blocked", "violations": exc.violations}

JavaScript / TypeScript

import { PolicyBlockedError } from '@execlave/sdk'; try {  await mcp.callTool({ name, arguments: args });} catch (err) {  if (err instanceof PolicyBlockedError) {    return { error: 'blocked', violations: err.violations };  }  throw err;}
§ 06

What's next