OpenTelemetry for MCP servers that actually catches silent failures.
MCP tools can return a successful JSON-RPC envelope with isError: true buried inside. Standard OpenTelemetry never sees it. opentel-mcp does — and marks the span ERROR.
pnpm add opentel-mcp- OpenTelemetry
- Model Context Protocol
- Node.js 20+
- TypeScript
- MIT licensed
- 136 tests
A tool can fail and your traces will still say it succeeded.
MCP's JSON-RPC 2.0 transport reports success at the envelope level even when the tool itself failed — the failure is nested inside CallToolResult.isError. Standard instrumentation only looks at the envelope.
Built specifically for MCP observability
How it works
Install
Add opentel-mcp alongside your existing OpenTelemetry SDK setup.
pnpm add opentel-mcpInstrument your server
Wrap your MCP server once — every tools/call gets a span automatically.
import { instrumentMcpServer } from "opentel-mcp";
instrumentMcpServer(server);See silent failures in your traces
Tool calls with isError: true now show up as ERROR spans with a stable fingerprint, in any OTel-compatible backend.
span: tools/call query_database
status: ERROR
fingerprint: a1b2c3d4e5f6a7b8How opentel-mcp compares
| Capability | opentel-mcp | @opentelemetry/api (raw) |
|---|---|---|
| Instruments tools/call automatically | ||
| Detects isError: true inside a successful response | ||
| Deep failure fingerprinting | ||
| Built-in mcp.tool.* metrics instruments | ||
| Cardinality-safe metric attributes, structurally enforced | ||
| Never throws into the instrumented handler | ||
| Requires manual span code per handler |
Raw @opentelemetry/api has no concept of MCP or CallToolResult — that's true by definition, not a knock against it. See the full comparison page for the reasoning behind every row, including why fastmcp and mcp-tracer aren't columns here.
Install in seconds
pnpm add opentel-mcpStop shipping silent MCP failures.
Install opentel-mcp and see the first ERROR span it catches that your current setup misses.
pnpm add opentel-mcp