Repository navigation
[BUG] Claude Desktop: hardcoded 60s initialize timeout drops stdio MCP servers whose package-runner cold start exceeds it #84136
Description
Summary
Claude Desktop cancels the MCP initialize request after exactly 60 seconds and there is no way to raise that limit. For stdio servers launched through a package runner (uvx, npx, pipx run), the download+install of dependencies happens inside that 60s window, before the server process exists. Any dependency refresh that pushes cold start past 60s therefore drops the server permanently for that session.
This is not a hypothetical: a routine upstream release of a transitive dependency (botocore, which ships a new version every business day) is enough to take a working configuration to a fully broken one overnight, with no change on the user's side.
Environment
- Claude Desktop 1.25927.0 (macOS 26.5.2, arm64)
- 3 ×
stdioMCP servers, identical launcher:uvx --with 'mcp[cli]<2' opensearch-mcp-server-py uv0.8.3
What happens
From ~/Library/Logs/Claude/mcp-server-<name>.log, identical in all three servers:
10:42:46.158Z [info] Initializing server...
10:42:46.170Z [info] Server started and connected successfully
10:42:46.194Z [info] Message from client: method="initialize" id=0
Downloading botocore (14.8MiB)
10:43:46.215Z [info] Message from client: method="notifications/cancelled" <-- +60.000s
10:43:46.216Z [info] Client transport closed
10:43:48.229Z [info] Server transport closed
The cancellation lands 60.000s after initialize — a fixed deadline, not a network failure. The servers' tools are then absent from the session entirely.
Measured by hand, same command, same env:
| Condition | Time to initialize response |
|---|---|
| Cold (dependency resolution + download of 68 packages) | 110 s |
| Warm (uv cache populated) | 7–26 s |
Warm start succeeds and the servers work perfectly. So the only thing standing between "works" and "silently gone" is whether an upstream package published a release since the last launch.
Root cause chain
- The MCP server package declares
boto3>=1.39.0— an open upper bound. botocorereleases daily (24 releases in July 2026 alone).uvxresolves to the newest match on each launch; a newbotocoreinvalidates the cached environment and triggers a fresh 14.8 MB download (occasionallynumpy/scipy/scikit-learntoo, ~50 MB total, 68 packages).- Cold install takes ~110 s > 60 s deadline →
notifications/cancelled. - All three servers race for the same uplink simultaneously, making each slower.
Evidence that step 3 happens routinely: the local uv cache holds 42 separate environments spanning 15 botocore versions (1.43.46 … 1.43.64), and has grown to 11 GB. botocore 1.43.64 was published at 20:12 the previous day; the failure above is from the next morning's first launch.
Why notifications/progress cannot solve this
The usual answer to a slow initialize is for the server to emit progress notifications and let the client extend its deadline. That is structurally impossible here. During the download there is no server process yet — uvx is still assembling the environment that will contain the server. Nothing exists that could send a notification. A client-side configurable deadline is the only thing that can help.
Why existing issues don't cover this
- [BUG] timeout field in claude_desktop_config.json not honored for MCP servers #43791, [FEATURE] Make MCP Tool Execution Timeout Configurable in Claude Desktop #22542, [BUG] Claude code does not obey values of MCP_TIMEOUT longer than 60 seconds #16837, MCP Server Timeout Configuration Ignored in SSE Connections #3033, [BUG] MCP Server Timeout Configuration Ignored in Streamable SSE HTTP Connections #20335 — all about tool call / request timeouts, not the
initializehandshake. - stdio MCP server tools dropped when initialize exceeds probe timeout #60224 — closest, but Claude Code CLI on Windows and a session-init probe; closed as not planned.
- Feature: Lazy MCP server initialization (don't block startup) #26666 — lazy init to unblock the prompt; closed as duplicate. Related but orthogonal: lazy init alone would still cancel this handshake.
- [BUG] Claude for Windows: MCP servers fail with UtilityProcess spawn timeout (hardcoded 5s spawn timeout) #64671 — Windows
UtilityProcessspawn timeout (5 s, OS-level), a different stage.
What is specific here: Claude Desktop, the initialize stage, and a server that has not started yet because its package manager is still installing it.
Steps to reproduce
- Configure a
stdioMCP server launched viauvxwith a package whose dependency tree is large and unpinned (any package depending onboto3works). - Run
uv cache cleanto simulate the state after an upstream dependency release. - Start Claude Desktop.
- Observe
Downloading …in the server log, thennotifications/cancelledat exactly +60 s, and the server's tools missing from the session.
Expected behaviour
Any one of these would resolve it:
- A configurable initialization timeout — honour a
timeout(orinitTimeout) field per server inclaude_desktop_config.json, or anMCP_TIMEOUTenv var, for theinitializephase. - Treat stderr activity as liveness — the child process is demonstrably alive and writing
Downloading botocore (14.8MiB)to stderr throughout. Resetting the deadline on output would fix the common package-runner case with no config. - Retry / late attach — if a server completes
initializeafter cancellation, register its tools rather than discarding them. The install finishes ~50 s later and the server sits there fully functional but unreachable. - At minimum, surface the cause in the UI. The server currently shows as failed with no hint that a download was cut short; a user reasonably concludes their backend is down and investigates in the wrong place.
Current workaround
Pin the package version so resolution stops picking up new transitive releases, and pre-warm the cache manually before starting the app. Both require knowing about the 60 s deadline in the first place, which is only discoverable by reading the logs closely enough to notice the timestamps are exactly 60.000 s apart.
Activity
qasimsethi1-svg commentedon Aug 14, 2026
Data point connecting this to #16837: the same 60 s
initializedeadline exists on the Claude Code CLI / local-agent path too, not just Claude Desktop — so a fix scoped to the desktop config file alone would leave the other half of the problem standing.On Windows (Claude Code 2.1.220, stdio server with
env.MCP_TIMEOUT = "180000"set in~/.claude/settings.json), the per-session MCP log shows the raised value being read as the connection budget while theinitializerequest is still killed at ~60 s:Full details in #16837 (comment) (cold start ~80 s from AV-scanned
node_modules; the same server connects in 12.7 s warm).Two takeaways for this issue:
sleep infinity,Connection failed after 60031ms) and the log above are both failures of the connect/initialize stage — same deadline as here, different launcher. The issues are the same underlying cap on two surfaces.MCP_TIMEOUTalready appears to govern this phase — the debug line prints the configured value as the connection budget — but the value never reaches theinitializerequest's own timer. So today the env var over-promises on both CLI and Desktop.The AV-cold-start case here is also a second everyday trigger besides package-runner downloads: real-time-scanned
node_modules(~12k files) pushes the first spawn past 60 s daily on managed corporate machines, and — like theuvxdownload — there's no progress notification to lean on for most of that window.alterbusinessmethod-afk commentedon Aug 24, 2026
Independent confirmation of this from a different trigger: boot-time resource contention, not just package-runner cold-download time.
Environment: Windows 365 Cloud PC, 4 vCPU, Claude Desktop (MSIX). Two stdio MCP servers measured: a Docker MCP gateway and a large Python-based server.
Data — same server, cold (first ~60-90s post-logon) vs warm (steady state), several runs:
initializeresponse timeThe failure signature matches the one already documented in this issue exactly: the renderer sends
notifications/cancelledat ~63s (fixed 60s deadline + a few seconds of transport overhead) and marks the server failed — not a network error, a clock. On a small cloud VM, the boot minute is enough contention on its own (CPU, disk, and process-table pressure from every other autostart item and MCP server initializing simultaneously) to blow a 60s budget that the same exact process clears in single digits of seconds once the machine settles. This is a different root cause than the "upstream dependency bumped, cold install exceeds 60s" case above, but the same defect: there is no way to raise theinitializedeadline for Desktop's stdio servers, so any source of one-time-slow startup (dependency refresh, or simply the machine being busy for the first minute after logon) permanently drops the server for the session with no retry.On the "why can't the server just send progress" question: it's worth noting the client side of that mechanism isn't actually wired up to help even in principle.
resetTimeoutOnProgressin the reference TypeScript SDK still defaults tofalseonmaintoday (two PRs proposing to flip the default — modelcontextprotocol/typescript-sdk#849 and modelcontextprotocol/typescript-sdk#1393 — were both closed without merging; the tracking issue, modelcontextprotocol/typescript-sdk#192, is still open). So even a server that does emitinitialize-phase progress notifications is relying on the client opting in to honor them, which neither the SDK default nor (going by the fixed ~63s cutoff observed here) Claude Desktop's client currently does.Given #43791 was closed as a duplicate of #16837 (the CLI-side
MCP_TIMEOUT-not-honored issue) and #16837's own thread has a Windows repro showing the raised timeout value being read for the connection budget but theinitializerequest still cut at exactly 60040ms — this looks like the same underlying fixed-60s ceiling on both the CLI and Desktop paths, just surfaced two different ways (env var ignored vs. no config knob at all). A per-serverinitialize-timeout override for Desktop (config-file field, same shape as the existing per-server tool-calltimeoutin.mcp.jsonper #76283) would fix both the dependency-cold-start case above and the boot-contention case here — neither is a network failure that should be treated as fatal-and-final.