MCP startup_timeout_sec does not cover pre-initialize OAuth bootstrap with saved tokens
What version of Codex CLI is running?
codex-cli 0.130.0
What platform is your computer?
Darwin 24.6.0 arm64
What issue are you seeing?
For Streamable HTTP MCP servers with saved OAuth credentials, Codex can hang before sending initialize when the OAuth metadata/bootstrap path is unreachable or hanging. startup_timeout_sec does not appear to apply to this pre-initialize OAuth path.
This is especially visible for VPN-only MCP servers:
- When the MCP is
auth_status: not_logged_in, Codex uses plain HTTP startup (initialize,tools/list) and fails fast if the VPN-only endpoint is unreachable. - When the same MCP is
auth_status: o_authbecause saved OAuth credentials exist, Codex performs OAuth bootstrap beforeinitialize. If VPN is off, it can hang before the normal MCP startup timeout applies.
Repro
Use a Streamable HTTP MCP config with a low startup timeout:
mcp_oauth_credentials_store = "file"
[mcp_servers.local_timeout_config_test]
url = "http://127.0.0.1:<port>/mcp"
startup_timeout_sec = 3
Run a local test server that:
- Accepts OAuth metadata requests like
GET /.well-known/oauth-authorization-server/mcpbut never responds, simulating a VPN-off / unreachable OAuth bootstrap path. - Allows unauthenticated MCP startup requests on
/mcp(initialize,tools/list) if Codex reaches them.
Compare two cases:
- No saved MCP OAuth token for this server.
- A saved MCP OAuth token exists in the configured MCP credentials store for this server.
Observed behavior
No saved token:
GET /.well-known/oauth-authorization-server/mcp
GET /mcp/.well-known/oauth-authorization-server
GET /.well-known/oauth-authorization-server
POST /mcp initialize no Authorization
POST /mcp notifications/initialized no Authorization
POST /mcp tools/list no Authorization
DELETE /mcp no Authorization
Codex eventually reaches plain MCP startup.
Saved OAuth token:
GET /.well-known/oauth-authorization-server/mcp
Codex never sends initialize or tools/list. In my controlled test, the process was still stuck after 12 seconds even though startup_timeout_sec = 3.
Expected behavior
Either:
startup_timeout_secshould cover pre-initialize OAuth transport/bootstrap work, or- the OAuth bootstrap path should have its own bounded timeout and return an actionable MCP startup error.
With startup_timeout_sec = 3, Codex should not remain stuck indefinitely before initialize.
Why this matters
A user who has previously run codex mcp login <server> can get worse startup behavior than a first-time/not-logged-in user. For VPN-only MCP servers, saved OAuth credentials cause Codex to enter a pre-initialize OAuth path that can hang when the VPN is off. The workaround is to run codex mcp logout <server> before starting Codex off VPN, which restores the plain startup/fail-fast path, but that is surprising and easy to miss.
Additional context
From reading the current open-source Codex code, the timeout seems to wrap the MCP handshake after the client/transport is created. In the saved-token case, Codex loads OAuth tokens and creates the OAuth-wrapped transport before the normal initialize/handshake timeout is applied. That appears to be the timeout boundary causing this behavior.
Related but not exact issues I found:
- #20167
- #13852
- #20729
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗