MCP servers marked "still connecting" should transition to ready/failed/timed-out (session-startup determinism)

Open 💬 0 comments Opened Jul 6, 2026 by fp2s92d5pd-bit

Problem

At session start, Claude Code emits a system-reminder listing MCP servers that are "still connecting":

The following MCP servers are still connecting — their tools (typically named mcp__<server>__*) are not yet available but will appear shortly:
loom
mcpdoc
mesh-chair-local
plugin:desktop-commander:desktop-commander
safari
serena
session-mcp
state-coverage-lint
xcode
xcodebuild

The reminder doesn't say how long "shortly" is, or what to do if a server never actually finishes. In our environment (23 MCP servers, mixed stdio + HTTP) two of the servers on this list fail deterministically on startup (missing env vars — LOOM_MCP_WORKSPACE, MCP_XCODE_PID) and they stay "still connecting" for the whole session. A turn-1 request that would use serena (Swift LSP) or xcodebuild can't reliably tell whether the tool is going to arrive.

Proposed direction

Transition each "still connecting" server to a terminal state within a bounded window, and expose the state to the model. The 2026-07-28 MCP RC's Tasks extension already defines the pending → answered state machine via InputRequiredResult — the same primitive can model MCP-server-startup as a pending task per server, with terminal states like ready, failed, timed_out, auth_required.

Two concrete requests:

  1. A follow-up system-reminder or per-server status once servers reach a terminal state — e.g. MCP servers ready: serena, xcodebuild. Timed out after 30s: loom, xcode. Failed: [connection refused: httpX]. The current system-reminder naturally reads as monotonic ("more tools will appear") without a way to know when the story is over.
  2. Distinguish authorized/authenticated/timed-out/failed so the model can route around a real failure instead of waiting. The MCP RC's WWW-Authenticate: Bearer resource_metadata=<url> on HTTP servers is one signal; -32020/-32021/-32022 on JSON-RPC is another; process exit code + stderr is a third for stdio. All three can share the terminal-state model.

Reference implementation (out-of-tree)

Ryonics implemented an equivalent probe locally as mcp_ready_probe.py (see projects/uplift-tools/mcp_ready_probe.py if you want to look — MIT-licensable if useful):

  • server/discover first (RC path)
  • 3-way response switch: DiscoverResult → modern; -32020/-32021/-32022 → version-mismatch (don't fall back); everything else → legacy initialize + notifications/initialized + tools/list
  • Nine terminal states (modern_rc, modern_version_mismatch, legacy_ready, legacy_partial, auth_required, auth_required_opaque, timeout, failed, probe_error)
  • Bounded budget: spawn=3s, first-response=4s, total=12s per server, parallel across the fleet — 23 servers in ~3.8s
  • Fail-open at every layer

The probe classifies the same servers this issue describes into actionable states (loom and xcode land as failed with stderr diagnostics), which is what Claude Code's own startup could do.

Success criteria

  • Every "still connecting" server reaches a terminal state within a bounded window (proposal: default ≤30s, configurable)
  • Model can distinguish ready / failed / timed_out / auth_required per server before turn 1 completes
  • Failing servers surface actionable stderr (or an equivalent error hint) so the model can tell the user or route around

Related

  • MCP RC (2026-07-28): blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/ — removes initialize handshake, on-demand server/discover, Tasks extension + InputRequiredResult
  • SDK betas (2026-07-28): blog.modelcontextprotocol.io/posts/sdk-betas-2026-07-28/ — dual-match -32602/-32002, Mcp-Method/Mcp-Name headers for identifying stalled server+tool
  • Claude Code changelog v2.1.152 (2026-05-27): SessionStart hook hookSpecificOutput.sessionTitle — same channel that could carry MCP posture

View original on GitHub ↗