fix: record JSON-RPC errors on client OpenTelemetry spans
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_codeon the active client span; - preserves the existing
ErrorDatatoMCPErrorconversion 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.pyuv run --frozen ruff format --check src/mcp/shared/jsonrpc_dispatcher.py tests/server/test_otel.pyuv 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.