Skip to main content
Model Context Protocol (MCP) is an open protocol that lets agents call tools, fetch resources, and receive prompts from independent server processes. The @arizeai/openinference-instrumentation-mcp package is unusual: it doesn’t emit any spans of its own. Instead, it propagates OpenTelemetry context across the MCP wire protocol so that spans created independently in the client and the server join into a single unified trace. That means you install MCPInstrumentation alongside another instrumentor that does emit spans (one of the OpenInference framework or LLM instrumentors) in both processes, and have both write to the same Arize AX project. This example pairs it with @arizeai/openinference-instrumentation-openai so the server’s tool emits an LLM span.
This is the TypeScript / JavaScript guide. For the Python instrumentor, see MCP.

Prerequisites

  • Node.js 18+
  • An Arize AX account (sign up)
  • An OPENAI_API_KEY from the OpenAI Platform (the server’s tool calls OpenAI, which supplies the span the instrumentor threads across the MCP boundary)

Launch Arize AX

  1. Sign in to your Arize AX account.
  2. From Space Settings, copy your Space ID and API Key. You will set them as ARIZE_SPACE_ID and ARIZE_API_KEY below.

Install

Install the union of client and server dependencies into the same project — the scripts below import each as needed:

Configure credentials

The client launches the server as a child process and forwards its environment, so both ends see the same ARIZE_PROJECT_NAME and write into the same Arize AX project.

Setup tracing

MCP instrumentation is manual on both sides — MCPInstrumentation can’t be auto-loaded because the MCP SDK has a non-traditional module structure, so you pass the stdio module explicitly. The client patches its stdio transport module and registers a tracer provider that exports to Arize AX:

The MCP server

The server has a parallel setup. Its instrumentation patches the server stdio module and adds OpenAIInstrumentation so the tool’s model call is captured as an LLM span. Because context is propagated across the MCP boundary, that span becomes a child of the client’s trace.
The server exposes one tool that answers a question with OpenAI. Two details matter for stdio transport: the server must not write to stdout (that’s the MCP wire channel), and it flushes its spans before returning so the exports land before the client tears the subprocess down.

Run MCP

The client uses StdioClientTransport to launch server.ts as a subprocess and pipe MCP traffic over stdin/stdout. You only run the client — it spawns the server automatically. The ocean_query span wraps the tool call; the server’s LLM span joins the same trace.
Run the example with npx tsx example.ts.

Expected output

Verify in Arize AX

  1. Open your Arize AX space and select project mcp-tracing-example.
  2. You should see a single new trace within ~30 seconds containing both client- and server-side spans: an ocean_query span from the client wrapping an OpenAI Chat Completions LLM span emitted by the server’s tool. The server’s span joining the client’s trace is the value MCPInstrumentation provides.
  3. If no traces appear, see Troubleshooting.

Check from the skill, CLI, or SDK

Confirm spans are actually reaching your Arize AX project. Use whichever fits your workflow — the skill and CLI work for any framework; the SDK check is shown for each language.
Install the Arize Skills plugin and let your coding agent check for you:
Then prompt your agent:
Use the arize-trace skill to export and analyze recent traces from my project. Confirm spans are arriving, and summarize any errors or latency issues.

Troubleshooting

  • No traces in Arize AX. Confirm ARIZE_SPACE_ID and ARIZE_API_KEY are set in the same shell that runs example.ts. Enable OpenTelemetry debug logs with export OTEL_LOG_LEVEL=debug and re-run.
  • Client and server show up as separate traces. MCPInstrumentation.manuallyInstrument(...) must run in both processes, before the MCP client/server modules are used — keep the instrumentation.ts / instrumentation_server.js import first in each entry point. If only one side is instrumented, context isn’t propagated and each side gets its own trace.
  • Server span missing from the trace. The server child is torn down as soon as the client has the tool result. Keep the await provider.forceFlush() inside the tool handler (before it returns) so the server’s spans export before the subprocess exits.
  • Connection closed from the client. Almost always something the server wrote to stdout before the MCP handshake completed. Keep server logging on console.error (stderr) — anything on stdout corrupts the MCP wire protocol over stdio.
  • 401 from OpenAI. Verify OPENAI_API_KEY is set and has access to the model in the example. The client passes env: process.env to the subprocess, so the single export OPENAI_API_KEY=... covers the server too.
  • Different LLM provider on the server. Swap the server’s openai.OpenAI() and model name for the provider you want, and add the corresponding OpenInference instrumentor to instrumentation_server.ts. The client’s instrumentation does not change.

Resources

Model Context Protocol Specification

OpenInference MCP Instrumentor (JavaScript / TypeScript)

MCP TypeScript SDK

MCP (Python) tracing