Late response to cancelled MCP request drops stdio transport instead of being ignored
Description
When an MCP server sends a JSON-RPC response for a request that the client has already cancelled via notifications/cancelled, Claude Code logs Received a response for an unknown message ID, closes the stdio transport, and reconnects.
The cancellation spec says senders should silently ignore these:
The sender of the cancellation notification SHOULD ignore any response to the request that arrives afterward
Reproduction
Any MCP server using mcp Python SDK 1.27.0 (which itself violates the receiver-side SHOULD by sending a response on cancel — separate issue against the SDK). Configure the server in ~/.claude.json, invoke a long-running tool (e.g. anything that does await asyncio.sleep), cancel it.
Sample log lines from ~/Library/Caches/claude-cli-nodejs/.../mcp-logs-<server>/<session>.jsonl:
Tool 'poll_build' failed after 57s: MCP error -32001: user-cancel
STDIO connection dropped after 279s uptime
Connection error: Received a response for an unknown message ID:
{"jsonrpc":"2.0","id":3,"error":{"code":0,"message":"Request cancelled"}}
Closing transport (stdio transport error: Error)
Impact
- Every cancelled MCP tool call costs 1–2 reconnects (~5–10s each, more if the server image has
--pull alwaysset). - Heavy use of long-running MCP tools puts the server in a near-permanent reconnect loop.
- Defends against malformed servers (the spec puts the SHOULD-not-send burden on the receiver, but resilient sender behaviour is also called out).
Suggested fix
When a JSON-RPC response arrives with an id that's no longer in the in-flight map, log at debug and drop the message instead of treating it as a transport error. Reserve the transport-drop behaviour for actual protocol violations (malformed JSON, etc.).
Environment
- Claude Code latest (Opus 4.7)
- macOS, stdio transport
- MCP server: any using
mcppython SDK 1.27.0
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗