MCP startup should not block tool listing or thread startup
Summary
A configured MCP server that is still starting or unreachable can block core Codex workflows when the session builds the aggregated MCP tool list. This makes optional MCP extensions behave like startup-critical dependencies.
Root cause found locally
codex-mcp currently waits on pending MCP clients from McpConnectionManager::list_all_tools() through AsyncManagedClient::listed_tools(). When an MCP server is unreachable and has no startup tool cache, the first turn/tool-router construction can wait until MCP startup timeout instead of keeping chat usable.
Proposed fix
I have a local/fork branch that changes listed_tools() to skip clients that are still initializing and have no cached startup tools. Cached startup tools are still returned while initialization continues; once startup completes, tools are listed normally.
Branch:
https://github.com/caokaizz/codex/tree/codex/mcp-nonblocking-tool-list
Compare:
https://github.com/openai/codex/compare/main...caokaizz:codex:codex/mcp-nonblocking-tool-list
Commit:
9502ad125c Avoid blocking tool listing on MCP startup
Validation run locally
just test -p codex-mcp list_all_tools_skips_pending_client_without_cached_tool_info_snapshotjust test -p codex-mcp(88 passed)just fmtjust fix -p codex-mcp
Note
I attempted to open a PR from the fork branch, but GitHub returned: caokaizz does not have the correct permissions to execute CreatePullRequest.
5 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
MCP tool discovery should probably be a degraded capability inventory, not a hard pre-turn dependency.
Each server row can carry last successful tool manifest, manifest hash, started_at, freshness, startup status, and timeout/error reason. The router can use cached tools when fresh enough, omit unknown servers for the current turn, and surface
MCP server pendingin diagnostics.---
_Generated with ax._
I hit what looks like the same issue today. Codex failed to start a new conversation, and removing one MCP server config made it work again.
This was hard to diagnose from the user side because the failure happened during conversation startup, and it was not obvious that an optional MCP server was blocking core Codex functionality.
This failure mode is worth treating as a dependency-classification problem, not only a timeout problem.
For startup, I would separate MCP servers into three states:
The important edge case is the first turn. If an optional MCP server is slow or unreachable, the user still needs a usable chat and a clear diagnostic: which server blocked, how long it waited, whether cached tools were used, and whether the server will be retried in the background.
For the proposed skip-pending behavior, I would also make the event observable in logs/UI. Otherwise users may see a model ignore a tool and misdiagnose it as a planning problem when the real cause was that the server was still initializing or had no startup cache.
A useful acceptance test would be: configure one fast MCP server and one unreachable optional server, start a fresh session, and verify that core chat plus the fast server are usable before the unreachable server timeout expires. Then verify the unavailable server can become visible later without restarting the thread.
+1, Pls fix it
This bug is stupid and wasting my token!