Malformed MCP response drops stdio transport instead of quarantining the server

Resolved 💬 1 comment Opened Apr 17, 2026 by thhart Closed May 25, 2026

Environment

  • Claude Code 2.1.112
  • macOS 26.4 (Darwin 25.4.0), arm64
  • stdio MCP transport bridging to SSE (Spring AI MCP server)

Symptom

Adding a misbehaving MCP server to ~/.claude.json prevents new Claude Code from starting cleanly — it presents to the user as a "crash" at startup. Removing the offending entry restores startup. Claude code is crashing hard!!!

Repro

Configure any MCP server whose stdio stream delivers a JSON-RPC response whose id the client did not issue (or a duplicate response for an already-consumed id — same effect).

From ~/Library/Caches/claude-cli-nodejs/<cwd>/mcp-logs-<name>/*.jsonl:

{"debug":"Starting connection with timeout of 30000ms"}
{"debug":"Successfully connected (transport: stdio) in 176ms"}
{"debug":"Connection established with capabilities: {\"hasTools\":true,...}"}
{"debug":"STDIO connection dropped after 0s uptime"}
{"debug":"Connection error: Received a response for an unknown message ID: {\"jsonrpc\":\"2.0\",\"id\":4,\"result\":{\"resourceTemplates\":[]}}"}
{"debug":"Closing transport (stdio transport error: Error)"}

In my case the root cause was a server-side bug (Spring AI MCP re-emitting the last response on concurrent POST batches as a duplicate SSE event), but the point is that any protocol-level violation from one MCP server should not degrade Claude Code startup — it should be isolated.

Expected

A protocol violation from one MCP server should:

  • log the violation (already happens)
  • mark that server's tools/prompts/resources unavailable for the session
  • leave the rest of the Claude Code session fully functional at startup

Observed

Startup experience degrades to the point the user reports it as a "crash" until the offending server is removed from ~/.claude.json.

Suggested fix

In the stdio MCP client, on "unknown message ID" or similar protocol errors: isolate the failure to that one transport, continue startup for everything else, and surface the failure via /mcp rather than letting it propagate into the startup path.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗