@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_KEYfrom the OpenAI Platform (the server’s tool calls OpenAI, which supplies the span the instrumentor threads across the MCP boundary)
Launch Arize AX
- Sign in to your Arize AX account.
- From Space Settings, copy your Space ID and API Key. You will set them as
ARIZE_SPACE_IDandARIZE_API_KEYbelow.
Install
Install the union of client and server dependencies into the same project — the scripts below import each as needed:Configure credentials
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 addsOpenAIInstrumentation 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.
Run MCP
The client usesStdioClientTransport 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.
npx tsx example.ts.
Expected output
Verify in Arize AX
- Open your Arize AX space and select project
mcp-tracing-example. - You should see a single new trace within ~30 seconds containing both client- and server-side spans: an
ocean_queryspan from the client wrapping anOpenAI Chat CompletionsLLM span emitted by the server’s tool. The server’s span joining the client’s trace is the valueMCPInstrumentationprovides. - 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.- Arize skill (agent)
- AX CLI
- SDK
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_IDandARIZE_API_KEYare set in the same shell that runsexample.ts. Enable OpenTelemetry debug logs withexport OTEL_LOG_LEVEL=debugand 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 theinstrumentation.ts/instrumentation_server.jsimport 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 closedfrom the client. Almost always something the server wrote to stdout before the MCP handshake completed. Keep server logging onconsole.error(stderr) — anything on stdout corrupts the MCP wire protocol over stdio.401from OpenAI. VerifyOPENAI_API_KEYis set and has access to the model in the example. The client passesenv: process.envto the subprocess, so the singleexport 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 toinstrumentation_server.ts. The client’s instrumentation does not change.