[BUG] -p/--print: stdio MCP servers stay "pending" — tool snapshot races server startup and never refreshes (regression 2.1.143 → 2.1.162)
Summary
In headless print mode (claude -p), stdio MCP servers configured via --mcp-config are spawned but never become usable: the system/init envelope reports mcp_servers[].status: "pending" with zero MCP tools in the tool registry, and the registry never refreshes for the rest of the session, so the model runs the entire session without the configured MCP tools.
This is a regression between 2.1.143 (works: status: "connected", all server tools present at init) and 2.1.162 (broken). Still broken in 2.1.201.
Interactive mode and claude mcp list connect to the same server on the same config without issue — this is specific to -p.
Environment
- Claude Code: 2.1.162 and 2.1.201 (broken); 2.1.143 (works) — same machine, same config, same server binary
- OS: Linux 7.0.0-27-generic
- MCP transport: stdio (local binary)
- Server: a Rust MCP server (rust-mcp-sdk 0.9) exposing 24 tools;
initialize→notifications/initialized→tools/listround-trip completes in <1s when driven manually
Reproduction
mcp.json:
{
"mcpServers": {
"myserver": {
"command": "/path/to/mcp-server-binary",
"args": ["--mcp", "-w", "/path/to/workspace"]
}
}
}
printf 'which tools do you have? names only' | claude -p \
--output-format stream-json --verbose --model haiku \
--dangerously-skip-permissions --no-session-persistence \
--strict-mcp-config --mcp-config mcp.json
Inspect the system/init event.
Expected
mcp_servers: [{"name":"myserver","status":"connected"}] and the server's tools present in tools (this is exactly what 2.1.143 emits for the same command).
Actual (2.1.162, 2.1.201)
mcp_servers: [{"name":"myserver","status":"pending"}], zero MCP tools in tools, and the model answers the turn using only built-in tools. No error output even with --debug.
Evidence that the server is healthy and actually spawned
claude mcp list(same config via project.mcp.json):myserver: ... - ✓ Connectedon the same broken CLI versions.- Wrapping the server command in a logging shim shows
-pdoes spawn the process with correct args; the server's stderr is clean. - Driving the server manually with the full
initialize→notifications/initialized→tools/listsequence returns all 24 tools in well under a second. - Ruled out:
--bare, prompt via stdin vs argument,MCP_TIMEOUT, sanitized environment (env -i), working-directory trust state, config passed as file path vs inline JSON vs project-scope.mcp.json. With a project-scope.mcp.jsonand no--strict-mcp-config, every configured MCP server (five different ones, stdio and remote) showspendingin-pmode.
Version bisect (same machine, same config, same server binary)
| CLI | system/init mcp_servers[].status | server tools at init |
|---|---|---|
| 2.1.143 | connected | 24 |
| 2.1.162 | pending | 0 |
| 2.1.201 | pending | 0 |
Likely mechanism / related
It looks like -p mode now snapshots the tool registry before MCP connections complete, and nothing refreshes the snapshot afterwards. #62844 documents the no-refresh half (tools/list_changed not honored in --print mode); this report is about the initial connection racing the snapshot, which combined with the no-refresh means stdio MCP is effectively unusable in headless mode on affected versions.
Impact
Any headless/scripted claude -p pipeline that depends on stdio MCP tools silently degrades: sessions run and "succeed" with built-in tools only, with no error surfaced. We caught it only because a benchmark smoke gate asserted mcp_tool_calls > 0.