Skip to main content
The Model Context Protocol SDK is the official toolkit for building MCP servers and clients. Braintrust traces requests and notifications on your MCP servers, and outgoing client requests in Python. Python also supports standalone FastMCP.
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 every McpServer 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

The auto-instrumentation example uses plain JavaScript so 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.
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 to wrapMCPServer(). 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, set captureContent 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
With auto-instrumentation, load the file before the hook:
With 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>.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
The 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 and wrapMCPServer() 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, with gen_ai.tool.name in 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>), with gen_ai.prompt.name in 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, and resources/read. With content capture on, resources/read spans 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, and notifications/message. mcp.notification.direction is incoming for notifications the server receives and outgoing for 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 initialize exchange (mcp.client.name, mcp.client.version, mcp.server.name, and mcp.server.version, plus title when 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 in rpc.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.

Resources