MCP client does not auto-reconnect after disconnect
Open 💬 5 comments Opened Feb 11, 2026 by lihah111222333-cloud
Summary
MCP server connections in Codex currently do not auto-recover after a disconnect, while model SSE streams already have retry/backoff logic.
This causes MCP tools/resources to remain unavailable until users manually reload MCP servers or restart the CLI.
Observed behavior
AsyncManagedClientuses a cachedShared<Future>startup result, so initialization is effectively one-shot.RmcpClienthasConnecting -> Readybut no explicitDisconnected/Reconnectinglifecycle.- There is no heartbeat/health monitor to proactively detect dropped MCP transport.
- Model API SSE flow has reconnect (
retry + backoff), but MCP path has no equivalent resilience. - Practical recovery is manual (
RefreshMcpServers/config/mcpServer/reload) or full process restart.
Why this matters
- Long-running Codex sessions with stdio or streamable-http MCP servers are fragile.
- Any transient server restart/network hiccup can permanently break MCP calls in the current session.
- Users experience random "tool call failed" errors without automatic self-healing.
Minimal repro
- Configure any MCP server (stdio is enough).
- Start a session and successfully call an MCP tool once.
- Restart/kill the MCP server process.
- Call MCP tool again.
- Expected: Codex reconnects and retries automatically.
- Actual: MCP call fails until manual reload/restart.
Suggested direction
- Add reconnect support to MCP connection management:
- Detect connection-class errors on MCP operations.
- Recreate client transport and re-run initialize/list-tools.
- Retry failed request once after successful reconnect.
- Re-send relevant state (e.g. sandbox-state capability update) after reconnect.
- Optionally add periodic health check/heartbeat for proactive reconnection.
If helpful, I can open a follow-up PR with a concrete implementation and tests.
5 Comments
Adding a startup-time variant of this that the "reconnect after disconnect" framing doesn't cover, plus a small complementary fix that already half-exists in the tree. (All against current
main.)A server that never connects in the first place. The repro here is "connect successfully, then kill the server." But the same one-shot
Sharedstartup future (observed-behavior #1) also makes the initial handshake terminal: if a non-required server'sinitialize+tools/listdoesn't finish withinstartup_timeout_sec(defaultDEFAULT_STARTUP_TIMEOUT = 30s) — e.g. a backend that's briefly slow on its firsttools/list— the future resolves toErrand is cached for the life of theMcpConnectionManager. In a long-lived / warm process the server then stays toolless for the rest of the session with no retry, even though a handshake a few seconds later would have succeeded. A reconnect-on-disconnect path wouldn't help, because the connection never reachedReady.There's already a survivorship mechanism — but only for one server.
AsyncManagedClient::listed_tools()servescached_tool_info_snapshotwhen the live startup result isn't usable (still initializing, orErr). That snapshot is only ever populated for the built-incodex_appsserver —load_startup_cached_codex_apps_tools_snapshotreturnsNonefor anyserver_name != CODEX_APPS_MCP_SERVER_NAME. Socodex_appsalready survives a transient startup failure; every user-configured server does not. Generalizing that snapshot to all servers would be a smaller, complementary fix to the full reconnect lifecycle proposed above — it specifically covers the cold-start / first-handshake case the reconnect path can't.Interaction with #28407. The in-flight "[codex-mcp] Avoid blocking on optional MCP startup" makes
list_all_toolsconcurrent and skips pending optional servers that have no snapshot (earlyreturn None). That fixes the first-turn latency in #19556 / #21318, but for the startup-failure case it turns "block" into "silently skipped, still toolless, still no retry" — so it doesn't address the durability concern here for non-codex_appsservers (and it removes the stall that previously made the problem visible).Failing-test outline (mirrors
list_all_tools_uses_cached_tool_info_snapshot_when_client_startup_fails, but for a generic server): build anAsyncManagedClientwith a non-codex_appsserver_name,cached_tool_info_snapshot: None,startup_complete = true, and aclientfuture resolving toErr(StartupOutcomeError::Failed { .. }), then assert its tools are recoverable. Today that path yieldsNoneand the server contributes no tools, so it fails — demonstrating the gap.Happy to put up a PR for the snapshot-generalization piece if that's a direction you'd take.
We really need something like this -- Claude has "reconnect" under /mcp. opencode has a solution. Currently, Codex has no great solution for MCP's flakiness
Please prioritize this, this issue is extremely disruptive and comes up frequently.
We're seeing this issue on our side as well
"Please prioritize this issue as it causes significant disruption and occurs frequently. We are experiencing the same problem on our side as well, and a timely resolution would be greatly appreciated."