StdIO MCP server disconnects after successful tools/call response when progress notifications are emitted
Summary
Claude Code disconnects a stdio MCP server after a successful tools/call response in a reproducible case where the request includes _meta.progressToken and the server emits progress notifications.
This appears to be a Claude Code client regression/compatibility issue, not a server crash:
- the same server remains stable under repeated direct MCP SDK client calls
- the server process does not throw or exit unexpectedly before responding
- Claude Code closes stdin shortly after the successful response
Claude Code version
2.1.119
Regression context
These changelog entries seem related:
- 2.1.105
- “Fixed stdio MCP server emitting malformed (non-JSON) output hanging the session instead of failing fast with \"Connection closed\"”
- “Fixed MCP tools missing on the first turn of headless/remote-trigger sessions when MCP servers connect asynchronously”
- 2.1.110
- “Fixed stdio MCP servers that print stray non-JSON lines to stdout being disconnected on the first stray line (regression in 2.1.105)”
This may be another stdio MCP regression in the same area.
What I expected
After a successful tools/call response, the stdio MCP server should remain connected and continue serving tools.
What happened
After a successful ping tool response, Claude Code removes the MCP tool registry for that server. The configured server may still show as connected via claude mcp get, but the tools are no longer callable in-session.
Repro characteristics
I narrowed this down to progress notifications:
- With progress notifications enabled for tool calls:
- Claude Code disconnects after a successful response.
- With progress notifications disabled:
- repeated live calls through Claude Code remain stable.
Evidence
Direct MCP SDK client is stable
The same server survives repeated:
tools/listtools/call(ping)tools/list
for 10 cycles under a direct MCP SDK client.
Claude Code-specific trace
The server writes file-only traces (not stdout/stderr). In the failing case, after a successful tools/call response, Claude Code closes stdin:
{"event":"transport.message","method":"tools/call","id":3}
{"event":"tools.call.received","name":"ping","params":{"_meta":{"progressToken":3,"claudecode/toolUseId":"..."}}}
{"event":"transport.send","method":"notifications/progress"}
{"event":"tools.call.execute.end","name":"ping","resultLength":10}
{"event":"transport.send","method":"notifications/progress"}
{"event":"tools.call.response","name":"ping","isError":false}
{"event":"transport.send","id":3}
{"event":"transport.send.done","id":3}
{"event":"stdin.end"}
{"event":"stdin.close"}
{"event":"process.beforeExit","code":0}
{"event":"process.exit","code":0}
In the stable case, with progress notifications disabled, Claude Code does not close stdin after the same successful response.
Minimal interpretation
Claude Code appears to be treating server-side progress notifications during a stdio tool call as connection-fatal for this server/client combination.
Additional notes
- This is not caused by stray stdout.
- Silencing routine stderr logging did not fix it.
- Disabling progress notifications did fix it.
Request
Please investigate Claude Code’s stdio MCP handling when:
_meta.progressTokenis present ontools/call- the server emits
notifications/progress - the server then returns a normal successful
tools/callresult
I’m happy to provide a minimal repro server if useful.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗