fix: record JSON-RPC errors on client OpenTelemetry spans

Closed
JS
2mo ago
+30-2

Summary

Fixes #3174.

When the MCP peer returns a JSON-RPC error, send_raw_request() receives the
ErrorData while the client OpenTelemetry span is active, but the span closes
before that response is converted to MCPError. Failed requests therefore look
like normal exits in client telemetry.

Changes

This change:

  • records StatusCode.ERROR, the JSON-RPC message, error.type, and
    rpc.response.status_code on the active client span;
  • preserves the existing ErrorData to MCPError conversion and its public
    exception contract; and
  • adds an in-memory regression test covering the client span and error code.

The scope is limited to client telemetry. Wire messages, request correlation,
timeouts, cancellation, connection-close handling, and handler exception
behavior are unchanged.

Verification

./scripts/test
uv run --frozen ruff check src/mcp/shared/jsonrpc_dispatcher.py tests/server/test_otel.py
uv run --frozen ruff format --check src/mcp/shared/jsonrpc_dispatcher.py tests/server/test_otel.py
uv run --frozen pyright src/mcp/shared/jsonrpc_dispatcher.py tests/server/test_otel.py
  • ./scripts/test (5581 passed, 10 skipped, 1 xfailed; 100% coverage)
  • uv run --frozen ruff check src/mcp/shared/jsonrpc_dispatcher.py tests/server/test_otel.py
  • uv run --frozen ruff format --check src/mcp/shared/jsonrpc_dispatcher.py tests/server/test_otel.py
  • uv run --frozen pyright src/mcp/shared/jsonrpc_dispatcher.py tests/server/test_otel.py

The changed files pass Pyright. A full macOS Pyright run also reports two
pre-existing os.waitid typing errors in
tests/transports/stdio/test_lifecycle.py:234, which is outside this diff.

AI assistance was used to help analyze and implement this change; I reviewed
the source and tests, reran the reproduction and verification, and take
responsibility for the contribution.