Enterprise·
advanced
·16 min read·Sep 12, 2026
By Rad Tome·Lead AI Systems Architect

MCP Telemetry, Observability and Distributed Tracing

Instrumenting Model Context Protocol (MCP) servers with OpenTelemetry (OTel), distributed context propagation, tool latency profiling, and SLO alerting.

opentelemetryobservabilitytracingmcpmonitoringprometheus
Interactive Tool
1-Click Export

Generate & Validate Multi-Client MCP Config

One-click export with environment variables & path locators for Claude Desktop, Cursor, Windsurf, and OpenAI Codex CLI.

Open in Generator

MCP Telemetry, Observability and Distributed Tracing

In complex multi-agent architectures, an agent prompt often triggers a chain of tool executions across multiple remote MCP servers before generating a final response. When an agent invocation takes 18 seconds or produces a corrupted output, diagnosing where the bottleneck occurred—in the LLM generation phase, network transport latency, database query execution, or an unhandled JSON-RPC error—becomes impossible without end-to-end distributed tracing.

Because the Model Context Protocol communicates over JSON-RPC 2.0 (via stdio, SSE, or Streamable HTTP), standard HTTP-only APM agents fail to capture internal tool lifecycles, schema parsing overhead, and parameter serialization costs.

This guide details how to implement full OpenTelemetry (OTel) observability across the MCP lifecycle, propagate W3C trace contexts through JSON-RPC metadata, profile tool latency budgets, and define Service Level Objectives (SLOs) for enterprise agent workloads.


1. End-to-End Tracing Architecture

A complete MCP trace spans the client orchestration framework (e.g., LangGraph, Claude Code, Cursor), traverses an API Gateway or Ingress, enters the target MCP Server, and terminates at backend dependencies (PostgreSQL, Pinecone, Redis).

mermaid
sequenceDiagram
    autonumber
    participant Host as AI Host (Agent / LLM)
    participant Gateway as MCP Gateway (Reverse Proxy)
    participant Server as Remote MCP Server
    participant DB as Production DB / Tool Target

    Note over Host,DB: Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736
    Host->>Gateway: POST /messages (Span: agent.tool_execution)
    Note over Host,Gateway: Injects W3C traceparent into JSON-RPC _meta
    Gateway->>Server: tools/call [db_select] (Span: gateway.forward)
    Server->>DB: SQL SELECT (Span: db.query)
    DB-->>Server: Query Result Rows (24ms)
    Server-->>Gateway: JSON-RPC Result (Span: mcp.tool_eval)
    Gateway-->>Host: Streamed SSE Chunk (Span: response.deliver)

Critical Trace Spans in MCP

  1. mcp.client.request: Captures the duration from the LLM model deciding to call a tool until the response is parsed back into context.
  2. mcp.gateway.route: Measures authentication validation, rate limit verification, and connection pool routing.
  3. mcp.server.tool_execute: Measures the exact execution time of the tool's underlying handler function.
  4. mcp.server.serialize: Captures the CPU overhead of converting large dataset results into token-dense markdown or JSON strings.

2. W3C Trace Context Propagation over JSON-RPC 2.0

Standard HTTP headers (traceparent, tracestate) do not persist when MCP servers operate over standard input/output (stdio) or when messages are multiplexed across persistent Server-Sent Events channels.

To maintain unbroken distributed traces across any transport, inject W3C trace contexts into the JSON-RPC _meta parameter object:

Outgoing tools/call with W3C Context Injection

json
{
  "jsonrpc": "2.0",
  "id": "trace-job-7712",
  "method": "tools/call",
  "params": {
    "name": "enterprise_db_query",
    "arguments": {
      "sql": "SELECT account_id, billing_tier FROM accounts WHERE status = 'active' LIMIT 10;"
    },
    "_meta": {
      "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
      "tracestate": "rojo=1,congo=2",
      "tenant_id": "org_enterprise_99",
      "agent_model": "claude-3-7-sonnet"
    }
  }
}

Result Payload with Child Span Propagation

The MCP server extracts the parent context, binds downstream database and API spans as children of 00f067aa0ba902b7, and returns execution metrics in the response metadata:

json
{
  "jsonrpc": "2.0",
  "id": "trace-job-7712",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "[{\"account_id\": \"acc_01\", \"billing_tier\": \"enterprise\"}]"
      }
    ],
    "_meta": {
      "execution_time_ms": 38.4,
      "span_id": "9a38f72c0199e4b1",
      "estimated_tokens": 42
    }
  }
}

3. Production OpenTelemetry Instrumentation (TypeScript)

Below is an OpenTelemetry SDK integration for an MCP server. It intercepts JSON-RPC calls, parses _meta.traceparent, instruments tool execution, and exports traces via OTLP/gRPC to Jaeger, Grafana Tempo, or Datadog.

typescript
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { 
  trace, 
  context, 
  propagation, 
  SpanStatusCode, 
  ROOT_CONTEXT 
} from '@opentelemetry/api';

// 1. Initialize OpenTelemetry SDK
const exporter = new OTLPTraceExporter({
  url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'https://otel-collector.internal:4317'
});

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'mcp-database-service',
    [SemanticResourceAttributes.SERVICE_VERSION]: '1.2.0',
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: 'production'
  }),
  traceExporter: exporter
});

sdk.start();

const tracer = trace.getTracer('mcp-instrumentation', '1.0.0');

// 2. Middleware: Extract Context and Trace Tool Execution
export async function executeMonitoredTool(
  toolName: string,
  args: Record<string, any>,
  meta: Record<string, any> = {},
  handler: (args: any) => Promise<any>
): Promise<any> {
  // Extract W3C traceparent from JSON-RPC _meta if available
  let parentContext = ROOT_CONTEXT;
  if (meta?.traceparent) {
    parentContext = propagation.extract(ROOT_CONTEXT, {
      traceparent: meta.traceparent,
      tracestate: meta.tracestate
    });
  }

  return context.with(parentContext, async () => {
    const span = tracer.startSpan(`mcp.tool_call:${toolName}`, {
      attributes: {
        'mcp.tool.name': toolName,
        'mcp.tenant.id': meta?.tenant_id || 'anonymous',
        'mcp.agent.model': meta?.agent_model || 'unknown',
        'rpc.system': 'mcp-jsonrpc'
      }
    });

    const startTime = performance.now();

    try {
      // Execute the actual tool logic
      const result = await handler(args);

      const duration = performance.now() - startTime;
      span.setAttribute('mcp.execution_duration_ms', duration);
      span.setStatus({ code: SpanStatusCode.OK });

      return {
        result,
        meta: {
          traceparent: span.spanContext().traceId,
          execution_time_ms: duration
        }
      };
    } catch (error: any) {
      span.recordException(error);
      span.setStatus({
        code: SpanStatusCode.ERROR,
        message: error.message || 'Tool execution failure'
      });
      throw error;
    } finally {
      span.end();
    }
  });
}

4. Key Performance Indicators (SLIs) and Prometheus Metrics

Enterprise SRE teams must track four critical Service Level Indicators (SLIs) across all MCP clusters:

Metric NameTypeDescriptionAlert Threshold
mcp_tool_execution_duration_secondsHistogramLatency distribution of tool executionp99 > 3.0s
mcp_tool_invocation_totalCounterTotal invocations segmented by tool and status5xx error rate > 1%
mcp_response_token_size_bytesHistogramPayload size returned back to LLM contextMax > 64KB
mcp_active_sse_connectionsGaugeConcurrent persistent client agent streamsCapacity > 85%

Prometheus Alerting Rules (mcp-alerts.yaml)

yaml
groups:
  - name: MCP_Observability_Alerts
    rules:
      - alert: MCPToolHighLatencyP99
        expr: histogram_quantile(0.99, sum(rate(mcp_tool_execution_duration_seconds_bucket[5m])) by (le, tool_name)) > 3.0
        for: 2m
        labels:
          severity: warning
          team: ai-platform
        annotations:
          summary: "MCP tool {{ $labels.tool_name }} p99 latency exceeds 3 seconds"
          description: "High tool latency directly blocks host LLM agent generation and exhausts connection pools."

      - alert: MCPHighToolFailureRate
        expr: (sum(rate(mcp_tool_invocation_total{status="error"}[5m])) / sum(rate(mcp_tool_invocation_total[5m]))) * 100 > 2.0
        for: 1m
        labels:
          severity: critical
          team: ai-platform
        annotations:
          summary: "MCP tool execution error rate > 2% across cluster"
          description: "Agents encountering repeated tool failures risk hallucination cascades and runaway retries."

5. Correlating Logs, Traces, and LLM Context Spans

When debugging agent misbehavior (e.g., an agent executing 15 consecutive failed queries), structured JSON logging must bind directly to OpenTelemetry trace IDs.

json
{
  "timestamp": "2026-09-12T19:45:10.112Z",
  "level": "warn",
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "span_id": "9a38f72c0199e4b1",
  "service": "mcp-database-service",
  "event": "tool_execution_slow",
  "tool": "enterprise_db_query",
  "duration_ms": 4120,
  "tenant": "org_enterprise_99",
  "query_fingerprint": "SELECT * FROM billing_events WHERE event_date = ?",
  "message": "Query exceeded soft threshold (3000ms). Index scan missing."
}

With trace_id attached to every log record, engineers can jump directly from a Grafana Loki or Datadog log entry to the complete Jaeger distributed trace flamegraph, visualizing the exact timeline of the multi-agent interaction.


6. Audit & Readiness Checklist

Before onboarding enterprise developers, ensure your telemetry pipeline verifies:

  1. Sensitive Parameter Redaction: Secrets, credit cards, and PII are scrubbed before tools/call arguments are emitted to OTel spans.
  2. Span Sampling Strategies: Implement tail-based sampling to retain 100% of failed and slow (>2s) tool calls while sampling normal introspection calls at 5%.
  3. Context Leak Prevention: Downstream servers must strip internal database credentials from error payloads before returning them in JSON-RPC format.

Related Production Guides

Ready to Deploy?

Build your full agent toolstack in the Visual Generator

Combine MCP Telemetry, Observability and Distributed Tracing with databases, search APIs, and memory graphs in a single configuration file.

Customize in Generator

MCP Telemetry, Observability and Distributed Tracing FAQ

What is the MCP Telemetry, Observability and Distributed Tracing?

Instrumenting Model Context Protocol (MCP) servers with OpenTelemetry (OTel), distributed context propagation, tool latency profiling, and SLO alerting.

How do I configure MCP Telemetry, Observability and Distributed Tracing in Claude Desktop or Cursor?

You can copy the configuration JSON from our guide or launch the interactive MCP Codex Config Generator at https://mcp-codex.com/generator to export valid configs in 1 click.

Can I use MCP Telemetry, Observability and Distributed Tracing with the OpenAI Codex CLI?

Yes, OpenAI Codex CLI supports Model Context Protocol. You can add it directly to ~/.codex/config.toml or pass arguments to codex mcp add.

RT

Written by Rad Tome

Lead AI Systems Architect & Founder, MCP Codex

@RadTome

Specializing in Model Context Protocol (MCP) integrations, autonomous AI agent orchestration, and distributed developer toolchains. Researches and benchmarks production MCP client-server architectures across OpenAI Codex, Claude, and Cursor.

Editorial Integrity: All configurations, schemas, and commands verified against live GitHub repositories and tested in local sandbox runtimes.

Related Guides