This page covers tracing MCP clients and servers that you build. To connect an MCP client, such as Claude Code or Cursor, to Braintrust, see Braintrust MCP.
Setup
Tracing requires Braintrust v3.42.0 or later, and@modelcontextprotocol/sdk v1.2.0 or later below v2.0.0, or @modelcontextprotocol/server v2.0.0 or later below v3.0.0. The examples on this page use the v2 server and client packages and require Node.js 20 or later.1
Install packages
2
Set environment variables
.env
Auto-instrumentation
To trace your MCP server without modifying your application code, initialize a Braintrust logger, then run your server with Braintrust’s import hook. Braintrust traces everyMcpServer and low-level Server that connects to a transport. MCP clients aren’t traced.1
Initialize Braintrust and start your server
mcp-server.js
2
Run with the import hook
node --import can run the file directly. The Braintrust APIs work the same in TypeScript projects. Compile your TypeScript to JavaScript and run the compiled file with the import hook, or run TypeScript directly with a TypeScript loader.If you’re using a bundler, see Trace LLM calls for plugin and loader setup.
Opt out of MCP tracing
Opt out of MCP tracing
If you use auto-instrumentation for other libraries but want to exclude MCP servers, add
mcp to the BRAINTRUST_DISABLE_INSTRUMENTATION environment variable.Manual instrumentation
To trace an MCP server manually, pass it towrapMCPServer(). wrapMCPServer() accepts an McpServer or a low-level Server and returns the same instance. It traces handlers registered before and after the call, and works before or after you connect the server to a transport.TypeScript
Omit arguments and results
MCP spans record tool and prompt arguments, resource URIs, tool, prompt, and resource results, and notification parameters by default.To keep tracing requests and notifications without recording this content, setcaptureContent to false in the mcp integration options of configureInstrumentation(). Import it from braintrust/instrumentation and call it in a separate file that loads before braintrust. A call from a file that imports braintrust has no effect.configure.mjs
wrapMCPServer(), import ./configure.mjs before any other import in your server’s entry file.When content capture is on, images and resource blobs in results are recorded as <omitted> unless you also set BRAINTRUST_CAPTURE_ATTACHMENTS to 1 or true, which uploads them as attachments. Audio and video content is always recorded as <omitted>.Link requests to the client’s trace
Each request your server receives starts its own trace. To nest a request under the span that sent it, the client must include W3C trace context (traceparent, and optionally tracestate and baggage) in the request’s params._meta. A Braintrust-instrumented client can produce these values with injectTraceContext(). Call it inside the client span that sends the request. Install the client package alongside the server dependencies:client.mjs
tools/call get_weather span nests under ask-weather in the client’s trace. See Trace distributed systems for how trace context propagates across services.What Braintrust traces
Auto-instrumentation andwrapMCPServer() produce the same spans, and spans that your handlers create nest under the request span. Braintrust records:- Tool call spans (
tools/call <tool>), recorded as tool spans, withgen_ai.tool.namein metadata. With content capture on, they also record the tool arguments as input and the tool result as output. - Prompt request spans (
prompts/get <prompt>), withgen_ai.prompt.namein metadata. With content capture on, they also record the prompt arguments as input and the prompt result as output. - Spans for other requests, named after the MCP method, such as
initialize,tools/list, andresources/read. With content capture on,resources/readspans also record the resource URI as input and the resource contents as output. - Notification spans, named after the MCP method, such as
notifications/initialized,notifications/progress, andnotifications/message.mcp.notification.directionisincomingfor notifications the server receives andoutgoingfor notifications it sends, and an outgoing notification sent for a request nests under that request’s span. - Progress and logging notification metadata, with progress values, level, and logger. With content capture on, notification spans also record the notification parameters.
- Request metadata, including
mcp.method.name,jsonrpc.request.id,mcp.session.id,mcp.protocol.version,mcp.resource.uri, and the transport protocol. - Client and server identity from the
initializeexchange (mcp.client.name,mcp.client.version,mcp.server.name, andmcp.server.version, plustitlewhen reported) on spans for the rest of the session. On HTTP transports without a session ID, later spans record only the identity and protocol version that each request and response report. - Errors, including tool results marked as errors, JSON-RPC error responses, handler exceptions, and connections that close before the response is sent, with the error category in
error.type. Spans for JSON-RPC error responses record the error code inrpc.response.status_code. Responses that reject the client’s request, such as method not found or invalid params, record the code without marking the span as an error. - Cancelled requests, marked with
mcp.cancelled.
Task requests (
tasks/* methods and task-augmented requests) aren’t traced.