A failed MCP server attach at startup is terminal for the session — no retry, even for transient network errors
Summary
When Claude Code spawns MCP servers at startup, a failed initialize is treated as terminal for the entire session. The server shows "Could not attach to MCP server <name>" and stays unavailable until the user manually restarts Claude Code.
There appears to be no retry and no reconnection attempt, even when the failure is obviously transient — a DNS hiccup, a laptop that has not finished associating with Wi-Fi, or a remote server mid-restart.
Why this is more than a papercut
The window that decides the outcome is very small. Measured against a remote MCP server over stdio:
- Healthy:
initializereturns in 0.42–0.94s,tools/list(62 tools) under 1.2s - Unreachable downstream: the proxy answers
initializewith a JSON-RPC error in ~0.2s, and the client gives up
So a network blip lasting a couple of seconds, coinciding with launch, costs MCP for the whole session. Nothing recovers on its own.
I measured how often that actually bites. A 12-hour probe of one remote MCP endpoint, once per minute:
720 samples
507 success (~0.2s)
213 failures, in three CONTIGUOUS blocks (1h42m, 1h50m, and one isolated sample)
The failures were local connectivity drops — a machine tethered to a phone that periodically left the building. Roughly 30% of that window was a hole a startup could land in, and several did. Each cost a session's MCP access, and the symptom ("Could not attach") looks like a server problem, so debugging goes to the wrong place.
Suggested behavior
Retry the attach with backoff rather than failing permanently. Options, roughly in order of value:
- Bounded retry at startup — e.g. 3–5 attempts over ~30s before declaring failure. Cheap, and covers the overwhelming majority of these.
- Background reconnection — keep retrying at a low rate after startup, so a server that becomes reachable at minute 3 attaches then, rather than requiring a restart.
- A user-initiated reattach that does not require restarting the whole session.
/mcpcurrently reports state; being able to retry from there would help even without automatic retry. - Distinguish the failure classes in the message. A connection/DNS error is transient; a missing binary or a config error is not. They currently present identically, which sends users to the wrong diagnosis.
Even (1) alone would eliminate nearly all of what I observed.
Workaround, for anyone hitting this
I patched the wrapper script that launches the server so it probes the endpoint's health URL and blocks until reachable (up to 60s) before exec'ing the MCP proxy. Verified three ways:
| Case | Result |
|---|---|
| Healthy endpoint | initialize 0.67–0.94s — no added latency |
| Unreachable | retries instead of dying in 0.2s |
| Unreachable, then recovers after 7s | waited 8.2s, attached successfully |
That third case is the one that was previously fatal.
This only works if you control the launch command and the server exposes a health endpoint. It cannot help for servers spawned directly, and it has to be repeated per server — which is why the durable fix belongs in the client.
Environment
- Claude Code 2.1.221, macOS
- Remote MCP server over a stdio proxy; same behavior observed against two different servers
- Reproducible by pointing an MCP server at an unreachable host and starting Claude Code