MCP stdio transport dropped on unrecognized notifications/progress token, corrupting in-flight tools/call

Resolved 💬 2 comments Opened Jun 1, 2026 by n0mad-ai Closed Jul 7, 2026

Summary

Claude Code tears down the entire MCP stdio transport when it receives a notifications/progress whose progressToken it does not (or no longer) recognize. An outbound tools/call that is in flight at that moment is aborted/corrupted, so the server receives the request without its arguments fully intact.

Symptom

A tools/call with a moderately large argument (a string field of ~600+ characters) intermittently fails with a server-side Zod validation error:

body: expected string, received undefined

i.e. the MCP server received the request but the large body field was missing. Immediately before the failure, the Claude Code MCP log shows:

Connection error: Received a progress notification for an unknown token:
  {progress:5, total:8, message:"Fusing rankings", progressToken:2}
STDIO connection dropped

Root cause (hypothesis)

On receiving a notifications/progress with an unrecognized/stale progressToken, Claude Code treats it as a connection error and drops the stdio transport. The in-flight outbound tools/call write is aborted/truncated, so the server parses a request with missing fields.

Per the MCP spec, progress notifications are advisory; an unknown or late progress token should be ignored, not cause a transport teardown.

Proven NOT server-side

Same MCP server, same payloads:

  • Direct HTTP POST to the backing daemon with a 510-char body → succeeds, every time.
  • A standalone @modelcontextprotocol/sdk stdio client (same SDK version, same server binary) with 600- and 700-char bodies → succeeds, every time.

Only the Claude Code client path fails, and only intermittently. There is no fixed size threshold — it is connection-state dependent.

Reproduction notes

  • Intermittent. Hard to force deterministically.
  • Claude Code serializes tool calls to a given MCP server: parallel tool_use blocks emitted in a single turn arrive at the server ~seconds apart (monotonically increasing JSON-RPC ids), so a "parallel burst" does not create concurrent wire pressure.
  • The failure correlates with notifications/progress frames emitted by a streaming tool (a long-running search) interleaved with other calls — the dropped token in the log ("Fusing rankings") is such a streaming progress message.

Expected behavior

An unrecognized or late notifications/progress token is ignored; the stdio transport stays up; the in-flight tools/call completes with its arguments intact.

Environment

  • Claude Code (latest)
  • MCP server over stdio
  • @modelcontextprotocol/sdk 1.29.0

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗