Desktop thread can omit configured remote OAuth MCP server from mounted tools with no auth prompt or error

Open 💬 6 comments Opened Apr 19, 2026 by gregorycarter
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

0.122.0-alpha.1

What subscription do you have?

Pro

What platform is your computer?

macOS 15.7.4 (24G517), arm64

What issue are you seeing?

A configured and enabled remote OAuth MCP server can be present in local Codex config, reachable on the network, and still be completely absent from the mounted tool catalog for a new thread.

In my case, the server is Bridge Town:

[mcp_servers.bridge_town]
enabled = true
url = "https://api.bridgetown.builders/mcp"

and it is also present in the local factory MCP config.

However, in a fresh Codex desktop thread, Bridge Town is not exposed as a callable tool at all. There is also no OAuth prompt, no auth-needed error, and no tool-mount error surfaced in the thread.

So from the user side this looks like: “MCP is enabled, server is up, but Codex silently does not mount it into the thread.”

What steps can reproduce the bug?

  1. Configure a remote OAuth MCP server in local Codex config, for example:
[mcp_servers.bridge_town]
enabled = true
url = "https://api.bridgetown.builders/mcp"
  1. Start a fresh Codex desktop thread in a trusted workspace.
  2. Ask Codex to use that MCP in the thread.
  3. Observe that the MCP tool namespace is not available in the thread at all.
  4. Observe that there is no OAuth login prompt and no explicit auth error.
  5. Independently verify the endpoint is reachable and advertising OAuth protection:
curl -I https://api.bridgetown.builders/mcp
HTTP/2 401
www-authenticate: Bearer resource_metadata="https://api.bridgetown.builders/.well-known/oauth-protected-resource"

What is the expected behavior?

One of these should happen:

  1. Codex mounts the MCP tools into the thread and lets me authenticate.
  2. Codex surfaces a clear “auth required / login needed” state for that server.
  3. Codex surfaces a clear tool-mount failure / MCP initialization error.

What should not happen is silent omission of the configured MCP from the thread’s callable tools.

Actual behavior

The thread only gets the built-in tools and the configured MCP never appears as a callable tool namespace.

In the failing thread, the mounted dynamic tools were only:

  • automation_update
  • read_thread_terminal
  • load_workspace_dependencies
  • install_workspace_dependencies

No Bridge Town or other MCP tool namespace was mounted.

Additional information

  • The relevant thread id on my machine was 019da3b9-640f-7153-ba9c-2b34b0a1db91.
  • Local config definitely contains the enabled server.
  • The Bridge Town endpoint was reachable and returned an OAuth-protected 401, so this does not look like a basic network outage.
  • The failure mode is specifically that the server is not exposed into the thread as tools, and the app gives no actionable reason.

This feels closely related to existing MCP exposure issues, but the important distinction here is:

  • remote HTTP MCP
  • OAuth-protected endpoint
  • server reachable
  • still not mounted into the thread tool catalog
  • no auth handoff / prompt / explicit error

If this is expected behavior, Codex should at least surface that clearly in the thread or MCP status UI.

View original on GitHub ↗

6 Comments

github-actions[bot] contributor · 4 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #16702
  • #18147
  • #16696

Powered by Codex Action

gregorycarter · 4 months ago

Additional local evidence from the failing machine/thread, in case it helps triage:

  1. This was not just list_mcp_resources being empty. I checked the local thread tool registry directly in Codex state.

For failing thread 019da3b9-640f-7153-ba9c-2b34b0a1db91, thread_dynamic_tools contained only:

  • automation_update
  • read_thread_terminal
  • load_workspace_dependencies
  • install_workspace_dependencies

There was no Bridge Town namespace and no other MCP-derived tool namespace mounted into the thread.

  1. I saw the same absence pattern across several earlier Bridge Town diagnostic threads on the same machine, not just one thread:
  • 019d53d2-de75-7542-8f47-c92b5cff9e4a
  • 019d86f6-69f9-7bf2-b21b-b29ef96c3f1d
  • 019d9f1b-8dc3-7623-adea-216e786819f0
  • 019da3b3-7332-7071-abb0-9b17d2225e42
  • 019da3b4-dabb-7a30-a129-978fcbe33746
  • 019da3b9-640f-7153-ba9c-2b34b0a1db91

So this does not look like a one-off thread corruption.

  1. The local config appears internally consistent:
  • ~/.codex/config.toml contains:
[mcp_servers.bridge_town]
enabled = true
url = "https://api.bridgetown.builders/mcp"
  • ~/.factory/mcp.json contains:
{
  "mcpServers": {
    "Bridge-Town": {
      "type": "http",
      "url": "https://api.bridgetown.builders/mcp",
      "disabled": false
    }
  }
}
  1. A basic reachability probe to the MCP endpoint succeeded and returned an OAuth-protected response:
  • HTTP/2 401
  • www-authenticate: Bearer resource_metadata="https://api.bridgetown.builders/.well-known/oauth-protected-resource"

That suggests the server is reachable and advertising the expected OAuth metadata, but Codex still never mounts it into the thread or offers an auth handoff.

This feels adjacent to:

  • #6465 (MCP servers not detected in Codex VS Code extension (but working in Codex CLI))
  • #14449 (Desktop app does not expose local stdio MCP server until cwd is set explicitly)

The difference here is that this is a remote HTTP MCP with OAuth rather than a local stdio server. The common symptom is silent non-exposure in the tool catalog.

Even if the intended behavior is “don’t mount until authenticated,” it would still be much better to surface that explicitly in the thread or MCP status UI rather than silently omitting the server.

gregorycarter · 4 months ago

I dug through the public codebase and there looks to be a real asymmetry between plugin MCP OAuth and ordinary configured remote MCP OAuth.

Likely path for this bug:

  • Desktop/app-server has a dedicated silent OAuth launcher for plugin MCP servers in codex-rs/app-server/src/codex_message_processor/plugin_mcp_oauth.rs via start_plugin_mcp_oauth_logins(...), which calls perform_oauth_login_silent(...) and emits McpServerOauthLoginCompleted.
  • Ordinary configured MCP servers appear to go through the generic startup path in codex-rs/core/src/session/mcp.rs -> codex-rs/codex-mcp/src/mcp_connection_manager.rs -> codex-rs/rmcp-client/src/rmcp_client.rs.
  • In RmcpClient::create_pending_transport(...), a streamable HTTP MCP server only gets an OAuth-aware transport if there are already stored OAuth tokens (or an explicit bearer token/header). If there are no stored tokens, Codex builds a plain unauthenticated StreamableHttp transport.
  • In StreamableHttpResponseClient::post_message(...), a 401 with WWW-Authenticate becomes StreamableHttpError::AuthRequired(...).
  • That bubbles up to MCP startup error handling in mcp_connection_manager.rs, which currently formats the auth-required case as: The {server_name} MCP server is not logged in. Run \codex mcp login {server_name}\.

So the probable product gap is:

  1. plugin MCPs get desktop-initiated silent OAuth,
  2. user-configured remote OAuth MCPs do not,
  3. and non-codex_apps MCPs also do not have the startup snapshot fallback used for codex_apps, so auth failure can leave them simply absent from the mounted tool list.

That matches the Bridge Town behavior here exactly: reachable OAuth-protected MCP endpoint, no auth prompt in desktop, and no mounted tool namespace in-thread.

I have not yet proven the final UI wiring end-to-end, so I’d phrase this as a strong root-cause hypothesis rather than a confirmed single-line fix, but the code paths above seem like the right place to look first.

Probable fix directions:

  • Reuse/generalize the app-server OAuth bootstrap used for plugin MCPs so ordinary configured remote OAuth MCP servers can initiate login in desktop sessions too.
  • Surface an explicit auth-needed status in the thread/tool UI instead of silently omitting the server.
  • Consider whether non-codex_apps remote MCPs need a better startup/tool-visibility fallback when auth is pending or incomplete.
gregorycarter · 4 months ago

Adding a more concrete diagnostic + patch sketch after reading a bit more of the OSS code/docs.

Diagnostic I’d add

  • When MCP startup for a configured StreamableHttp server fails because StreamableHttpError::AuthRequired(...) bubbles up during handshake, emit a first-class “auth required / not logged in” reason all the way to app-server / UI instead of only formatting the generic startup error string in mcp_init_error_display(...).
  • In practice, I’d log and/or surface at least:
  • MCP server name
  • transport kind (streamable_http)
  • whether OAuth discovery says login is supported (oauth_login_support(...) / discover_streamable_http_oauth(...))
  • auth status (NotLoggedIn vs OAuth vs BearerToken)
  • whether startup omitted the server from mounted tools because auth is pending
  • This would make failures diagnosable without needing sqlite inspection of thread_dynamic_tools.

Why I think the fix should be in desktop/app-server wiring

  • The app-server docs already define an explicit request for configured MCP login: mcpServer/oauth/login.
  • The protocol surface already includes:
  • McpServerOauthLoginParams = { name, scopes?, timeoutSecs? }
  • McpServerOauthLoginResponse = { authorizationUrl }
  • McpServerOauthLoginCompletedNotification = { name, success, error? }
  • So this looks less like “missing backend capability” and more like “desktop/TUI is not invoking the existing configured-MCP OAuth flow when startup hits auth-required.”
  • In contrast, plugin MCPs have a separate server-side silent OAuth path in codex-rs/app-server/src/codex_message_processor/plugin_mcp_oauth.rs, which explains the asymmetry.

Lowest-risk patch sketch

  1. Detect the specific auth-required startup case for configured remote MCPs.
  2. When auth status is NotLoggedIn and OAuth login is supported, have the desktop client/app-server path trigger the existing mcpServer/oauth/login flow for that server instead of silently allowing tool exposure to collapse to zero.
  3. Open the returned authorizationUrl in the frontend, then wait for mcpServer/oauthLogin/completed.
  4. On successful completion, immediately refresh MCP server state/tool exposure (config/mcpServer/reload and/or thread MCP refresh) so the server is re-mounted without requiring the user to manually restart / create guess-and-check threads.
  5. If login is declined or fails, keep the server visible in status/UI as “login required” rather than omitting it from the mounted tool set with no explanation.

Alternative patch if you want the smallest UX-only change first

  • Even before auto-login, preserve a visible auth-needed status in thread/app UI for configured MCPs that failed startup with auth-required.
  • That alone would avoid the current silent failure mode.

Regression test suggestion

  • Add an app-server integration test using the existing codex-rs/app-server/tests/common/mcp_process.rs harness and a stub StreamableHttp MCP endpoint that:
  • advertises OAuth support,
  • returns 401 + WWW-Authenticate before login,
  • succeeds after a simulated login/token write.
  • Assert that a fresh thread does not silently lose the server with no signal.
  • Preferred assertion for the full fix: startup leads to mcpServer/oauth/login (or equivalent auth-needed status), then after simulated completion + reload, mcpServerStatus/list / mounted tools include the server.

This feels like a good candidate for an invited PR if maintainers agree on whether the desired UX is auto-triggered login vs explicit “Connect MCP” UI first.

kkondaurov · 2 months ago

The behavior still persists and is maddening.

jcostello092604 · 1 day ago

Adding a Windows reproduction that appears to match this issue.

Environment

  • Codex Desktop 26.820.7780.0
  • Windows x64
  • Three configured remote OAuth MCP servers

Observed behavior

  • codex mcp list shows all three remote servers as enabled with Auth = OAuth.
  • In fresh Codex Desktop tasks, GitHub tools are attached and callable, but none of the three remote OAuth MCP tool catalogs are mounted.
  • No authentication-needed or tool-mount error is surfaced.
  • Re-running login for all three servers and fully restarting Codex Desktop does not restore tool attachment.
  • Removing an unrelated local stdio MCP server and restarting does not change the result, so coexistence with that local server does not appear causal.
  • The same remote services were attached and callable previously. All three read-only API probes also succeeded in a task where their tools were attached.

There may also be a separate post-restart empty or stale turn symptom consistent with #40915, but the reproducible issue here is the silent omission of authenticated remote OAuth MCP tools.

Expected behavior

Configured, enabled, authenticated remote MCP tools should be mounted in fresh tasks. If mounting fails, Codex should expose a per-server diagnostic explaining whether discovery, OAuth credential resolution, initialization, or tool-catalog attachment failed.

Could the maintainers add per-task MCP diagnostics that show why an enabled server was omitted from the callable tool catalog?