TUI hangs forever at 'Loading MCP (x/y)' when 'resume --remote' has -c mcp_servers.* overrides the app-server doesn't have
What version of Codex CLI is running?
0.144.1 (npm) — also reproduced on 0.138.0
What subscription do you have?
n/a — the hang happens during MCP loading, before any model/auth interaction
Which model were you using?
n/a
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64 (Windows 11 Pro)
What terminal emulator and version are you using (if applicable)?
Windows Terminal / plain cmd.exe (reproduces in both)
Codex doctor report
not available
What issue are you seeing?
codex resume --remote <ws-url> <thread-id> hangs forever at Loading MCP (x/y) when the client command carries -c mcp_servers.* config overrides that the app-server it attaches to was not started with. The TUI never becomes usable; it stays on the MCP loading indicator indefinitely (waited 10+ minutes).
Notes from debugging:
- The configured MCP server itself is healthy (a local streamable-HTTP server that answers in milliseconds). While the TUI is frozen, no TCP connection is ever opened to the configured MCP URL, so it is not a slow/unresponsive server — it looks like a deadlock in the remote-attach handshake around the config/MCP sync.
RUST_LOG=codex_core=debug,rmcp=traceon the app-server shows nothing relevant after the attach (only unrelated plugin-manifest warnings at startup).
What steps can reproduce the bug?
Using any existing thread id (create one with a plain codex session first):
Terminal A — start an app-server without the MCP override:
codex -c check_for_update_on_startup=false app-server --listen ws://127.0.0.1:7777
Terminal B — attach to it with an MCP override on the client:
codex resume --remote ws://127.0.0.1:7777 <thread-id> -c mcp_servers.example.url=http://127.0.0.1:64442/stream -c mcp_servers.example.tool_timeout_sec=3600 -c mcp_servers.example.http_headers.X_AUTH=sometoken
→ stuck at Loading MCP (x/y) forever.
Full matrix tested (same thread, same machine):
| app-server flags | resume client flags | result |
|---|---|---|
| none | none | ✅ works |
| none | mcp_servers.* overrides | ❌ frozen at Loading MCP |
| mcp_servers.* overrides | none | ✅ works (and the MCP server's tools are available in the session) |
| mcp_servers.* overrides | same overrides | ✅ works |
A plain local codex resume <thread-id> (no --remote) with the exact same mcp_servers.* overrides also works fine — the hang is specific to remote attach.
What is the expected behavior?
Client-side mcp_servers.* overrides on resume --remote should either be applied to the session, or be ignored with a warning — not deadlock the TUI at MCP loading.
Additional information
Found via an IDE integration that spawns the app-server itself and attaches a TUI with an injected per-session MCP server config; the repro above is fully standalone (no IDE involved). Happy to run further diagnostics on request.
2 Comments
I reproduced this independently with a macOS Codex 0.144.1 TUI connected over WSS to a Linux remote app-server.
All six remote MCP startup streams reached a terminal
readystate, and a protocol relay confirmed that every status notification reached the macOS client, but the TUI remained frozen at5/6. Launching the same remote session with an isolated clientCODEX_HOMEcontaining no local MCP entries completed immediately.Root cause
The TUI seeds
mcp_startup_expected_serversfrom the client's localconfig.mcp_servers, even forAppServerTarget::Remote. Completion requires every expected name to appear in the app-server status stream. A client-only MCP name can never be reported by the remote app-server, making the completion predicate permanently false.This also explains why bubblewrap/zAppGuard and the individual MCP transports appeared suspicious but were not causal: the remote servers had already initialized and their terminal statuses were delivered.
Proposed patch
Commit: https://github.com/mjlbach/codex/commit/9431af2abc371603a62010168011ee4046f6c977
Compare: https://github.com/openai/codex/compare/main...mjlbach:codex:agent/fix-remote-mcp-startup
For remote workspaces, the patch uses the existing reported-server behavior (an empty configured expected set), so startup settles from the servers actually reported by the app-server. Embedded sessions retain the existing config-derived completeness guard. It intentionally does not forward client MCP overrides to the remote app-server.
<details>
<summary>Patch (code and regression test)</summary>
</details>
Validation
just fmtjust test -p codex-tuijust fix -p codex-tuiThe full
codex-tuisuite and required Clippy autofix completed successfully.I can reproduce this locally with Codex 0.147.0 on both the client and the NAS.
Reproduction
I start the NAS app-server and expose it through an SSH local forward:
Then I attach the local TUI:
The local client has these enabled MCP entries:
ai-drawiocodegraphlean-lspnode_replwolframAIgithubopenaiDeveloperDocsThe NAS app-server has only:
brave-searchgithubThe SSH tunnel and WebSocket connection are healthy:
initialize, thread resume, and the app-server RPCs complete successfully. The NAS reportsbrave-searchandgithubthroughstarting -> ready, but the TUI remains stuck atLoading MCPindefinitely because the client-only MCP names never appear in the remote status stream.Disabling the local-only MCP entries with
-c mcp_servers.<name>.enabled=falseallows the remote TUI to start. This reproduces the same local-vs-remote expected-server mismatch described in the root-cause analysis above.The proposed fix in Avoid local MCP gating for remote startup also appears to address this exact reproduction.