Skip to main content
Available via: auto-instrumentation of the official MCP client packages (TypeScript and Python), plus mcpServerTool() / @mcp_server_tool for tools in your own MCP server. Nothing to configure; one switch turns it off.
When your agent calls a tool through an MCP client, the call can fail in four different places: the model picked a tool the server doesn’t list, the model sent arguments that don’t fit the tool, the server answered with an error, or the connection broke. A plain tool span can’t tell these apart. The SDK records enough on each call, in your process, for Zespan to say which one it was.

What is traced

Each call through a patched MCP client becomes an mcp_client span (see Span kinds):
  • “Was the tool listed” needs a tools/list call earlier in the same client. A call made before any list is recorded without this answer (never as “not listed”), and so is a call made when only the first page of a paged list was read.
  • “Do the arguments fit” is checked in your process against the schema the server sent: required keys and JSON types only. The answer is true, false or unknown (when the schema uses something the SDK doesn’t check). false is recorded only for a definite miss.
  • A result the server flagged as an error is recorded as a tool error, not as a connection failure. A closed connection or a timeout is recorded as such. Servers differ in how they report an unknown tool (an error result on some, a JSON-RPC error on others); the recorded “was the tool listed” answer is the same either way.
  • Hashes are of the schema with the serializer’s noise removed ($schema and a top-level additionalProperties: false), so upgrading the library a server is built with doesn’t look like the tool list changing.
Only ids, names, hashes, counts, codes and true/false answers leave your process. Tool arguments, results and schemas are never sent by this feature, and the error message of a failed call is attached only when tool input and output storage is on for the project, with URL credentials, query strings and bearer tokens removed first. A call you cancel yourself (an aborted signal in TypeScript, a cancelled task in Python) is not recorded as a failure.

Supported clients

@modelcontextprotocol/sdk (1.x) and @modelcontextprotocol/client (2.x), either or both. Tested with @modelcontextprotocol/sdk 1.30.1, 1.31.0 and 1.32.1 and @modelcontextprotocol/client 2.3.1. Both are optional peer dependencies: nothing is installed for you.
The SDK finds the client by its methods, not by its version number: a client that doesn’t have the methods it needs is left alone rather than guessed at. Patching never changes what your code gets back: the result and the error are exactly what the MCP client produced (an error nobody handles stays unhandled), and a problem inside the SDK is swallowed. In TypeScript your code resumes one microtask later than without the SDK.

Python: one record per call

Version 2 of the mcp package emits its own OpenTelemetry client span for every request. When Zespan’s OpenTelemetry exporter is installed and pointed at Zespan (enable_otel=True with otel_endpoint set), that span reaches Zespan, so the SDK adds its attributes to it (the span is closed a moment later, after the result is known) and sends nothing else. Without the exporter, inside a Zespan trace (where the library’s span would sit in another trace), or for a call you cancelled, the SDK leaves the library’s span alone and records the call itself. Either way a call is recorded at most once.

Turn it off

Set mcpAutopatch: false (TypeScript) or mcp_autopatch=False (Python) in the SDK options, or set the environment variable ZESPAN_MCP_AUTOPATCH=off. It is read at call time, so you can switch it without restarting a long-lived process that already created clients.

Your own MCP server

Wrap the handler of a tool in your own MCP server so each call is an mcp_server_tool span that joins the caller’s trace from the request’s _meta.traceparent. Model calls and other work inside the handler nest under it.
The handler’s result and errors pass through unchanged (sync and async). A missing or malformed traceparent is simply not joined; the call is still recorded in the current trace.

Tools from MCP adapters

Tool spans from an MCP adapter in Google ADK, LangChain and the Vercel AI SDK carry the tool name as mcp_tool_name (and zespan.mcp.server when the adapter names the server). No span is added. An adapter is recognised by its class name (ADK’s McpTool), its module in Python, or by an mcp_server key in the tool’s metadata. The LangChain MCP adapters build ordinary structured tools, so for LangChain add mcp_server to the tool’s metadata; for the Vercel AI SDK, put it in experimental_telemetry.metadata. ADK and the LangChain adapters call the official MCP client underneath, so the same call is usually also an mcp_client span.

Where it shows up

  • The spans appear in the trace view with the span kind mcp_client (or mcp_server_tool).
  • Each tools/call is also stored as an MCP tool call, with the server name and version, the JSON-RPC error code and the schema hashes.
  • Zespan uses the recorded answers (tool listed, arguments valid, error code, tool error, connection failure, tool list changed together with a server version change) to say whether a failed MCP call started with the model or with the MCP server.

Limits

  • A tool the model provider runs on its own servers (a hosted MCP tool inside a model call) never passes through your client and can’t be seen.
  • Whether the protocol version is recorded depends on the client library: version 1 of the TypeScript client has no accessor for it, so it is recorded only when the transport reports it.
  • The OpenTelemetry conventions for MCP are still changing; Zespan maps the current attribute names and keeps the older error-code name readable.

Next steps

Google ADK

Agents built with ADK, including MCP toolsets.

Span kinds

Where mcp_client and mcp_server_tool sit among the other kinds.

Environment variables

The ZESPAN_MCP_AUTOPATCH switch.

OpenTelemetry

Send MCP spans from any OpenTelemetry setup.