Skip to content

Codex Desktop authenticates OAuth MCP server but never imports tools into threads; auth_status stays unsupported #20009

Open

Description

@Matt-TravelStorys

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

Codex Desktop 0.125.0-alpha.3 is shown in local session metadata as the active CLI/app runtime.

What subscription do you have?

Unknown from the app logs on this machine.

What platform is your computer?

macOS (Darwin, Apple Silicon). The workspace is local.

What issue are you seeing?

Codex Desktop successfully adds and authenticates a streamable HTTP MCP server, but never surfaces that server's tools in any thread.

What works:

  • codex mcp add <server-name> --url <streamable-http-mcp-url>
  • codex mcp login <server-name>
  • OAuth completes successfully multiple times
  • codex mcp list --json shows the server is present and enabled
  • Manually calling tools/list with the same bearer token Codex obtained returns the expected tool list from the MCP server

What fails:

  • codex mcp list --json reports "auth_status": "unsupported"
  • Codex Desktop threads never receive any dynamic tool entries for the configured MCP server
  • Restarting Codex Desktop does not help
  • Removing the server, re-adding it, and re-authing from scratch does not help

This makes the server look configured and authenticated, but it is never actually imported into the thread tool registry.

Steps to reproduce

  1. Add a streamable HTTP MCP server that advertises OAuth:
    • codex mcp add <server-name> --url <streamable-http-mcp-url>
  2. Authenticate it:
    • codex mcp login <server-name>
  3. Complete the browser OAuth flow
  4. Start a new Codex Desktop thread or restart Codex Desktop
  5. Ask the thread whether the MCP server's tools are available

Expected behavior

After successful OAuth, Codex Desktop should import the MCP server's tools into new threads.

Actual behavior

The server remains visible in MCP config, but no tools from that MCP server are exposed in threads.

Evidence

codex mcp list --json returns:

[
  {
    "name": "<server-name>",
    "enabled": true,
    "disabled_reason": null,
    "transport": {
      "type": "streamable_http",
      "url": "<streamable-http-mcp-url>",
      "bearer_token_env_var": null,
      "http_headers": null,
      "env_http_headers": null
    },
    "startup_timeout_sec": null,
    "tool_timeout_sec": null,
    "auth_status": "unsupported"
  }
]

~/.codex/config.toml contains:

[mcp_servers.<server-name>]
url = "<streamable-http-mcp-url>"

Local thread state shows no dynamic tool entries for the configured MCP server at all. Recent threads only have built-in tools like:

  • automation_update
  • read_thread_terminal
  • load_workspace_dependencies
  • install_workspace_dependencies

In the local SQLite state DB (~/.codex/state_5.sqlite), the thread_dynamic_tools table never gets an entry for the configured MCP server in recent threads.

Also, the server itself appears healthy:

  • Unauthenticated tools/list returns 401 Unauthorized
  • The server advertises OAuth metadata correctly
  • Manual tools/list with the same Codex-issued bearer token succeeds and returns the expected MCP tool list

That suggests the server is not the problem; the issue appears to be in Codex Desktop/CLI auth classification or MCP tool import after login.

Additional notes

  • codex mcp login <server-name> succeeds repeatedly, but codex mcp list --json still reports "auth_status": "unsupported"
  • There is no legacy ~/.codex/.credentials.json on this machine
  • The visible ~/.codex/auth.json does not expose a named top-level credential entry for the configured MCP server

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    mcpIssues related to the use of model context protocol (MCP) servers
    authIssues related to authentication and accounts
    on Apr 28, 2026
  2. removed
    appIssues related to the Codex desktop app
    on Apr 28, 2026
  3. jm-fhc commented on Jun 2, 2026

    @jm-fhc

    I am seeing what looks like the same MCP tool-injection failure on macOS / Codex Desktop.

    Environment:

    • macOS 26.5 (25F71)
    • Codex CLI/runtime: codex-cli 0.136.0-alpha.2
    • Product surface: Codex Desktop

    Configured streamable HTTP MCP servers:

    • jt-datax at https://ai.winyourdata.com/mcp
    • jobtread_native at https://api.jobtread.com/mcp

    Observed behavior:

    • codex mcp list shows both servers enabled.
    • Both show Auth: Unsupported.
    • codex mcp get jobtread_native shows enabled: true, transport: streamable_http, and the expected URL.
    • The active thread is a user thread with local cwd /Users/joshmcculloch/Documents/Codex.
    • Current thread tool discovery does not expose either server's tools.
    • The local thread_dynamic_tools table has no JobTread MCP tools for the active thread.

    This does not appear to be only a server-health/config issue: after OAuth, a fresh post-login thread previously exposed the DATAx JobTread tools and successfully completed a minimal read-only organization metadata call. Later, the long-running coordination thread could no longer see either the DATAx tools or the newly configured native JobTread tools.

    Impact:

    • Long-running coordination / command-centre threads lose the ability to refresh JobTread current state.
    • Creating a fresh thread as a workaround defeats the purpose of a persistent coordination thread and loses context.
    • From inside the agent, this can be confused with "no relevant source data" unless the agent separately inspects MCP config and thread tool metadata.

    Requests:

    • Add a diagnostic that distinguishes: MCP server configured, authenticated, initialized, tools discovered, tools injected into current thread, and tool call executable.
    • Clarify whether existing threads should refresh MCP tools after OAuth/config changes.
    • Fix or document why enabled streamable HTTP MCP servers can show Auth: Unsupported and fail to inject tools into threads.
  4. JohnnyDevNull commented on Jun 3, 2026

    @JohnnyDevNull

    I have the same problem with the GitLab MCP Server authenticated with OAuth.
    It's shows up connected in the mcp server overview in the desktop app, but not available in the Session.

    When I do the login via console it works just one time but after that not accessible through any session or thread because of an json-parse error.

    On one of my colleague's MacBook Pro this works perfectly fine with Claude Code Desktop, but not with Codex on my MacBook.

    The first step works:

    codex mcp add --url "https://gitlab.com/api/v4/mcp" GitLab
    
    Added global MCP server 'GitLab'.
    Detected OAuth support. Starting OAuth flow…
    Authorize `GitLab` by opening this URL in your browser:
    https://gitlab.com/oauth/authorize?response_type=code&client_id=[...]
    
    Successfully logged in.
    

    A mcp login then wants another OAuth again:

    codex mcp login GitLab
    
    Authorize `GitLab` by opening this URL in your browser:
    https://gitlab.com/oauth/authorize?response_type=code&client_id=[...]
    
    Successfully logged in to MCP server 'GitLab'.
    

    The mcp list shows it as enabled after that:

    codex mcp list
    
    Name       Url                               Bearer Token Env Var  Status   Auth 
    GitLab     https://gitlab.com/api/v4/mcp     -                     enabled  OAuth
    atlassian  https://mcp.atlassian.com/v1/mcp  -                     enabled  OAuth
    

    Now when i open the cli it shows an error:

    ⚠ MCP client for `GitLab` failed to start: MCP startup failed: handshaking with MCP server failed: Send message error Transport [rmcp::transport::worker::WorkerTransport<rmcp::transport::streamable_http_client::StreamableHttpClientWorker<rmcp::transport::auth::AuthClient<codex_rmcp_client::http_client_adapter::StreamableHttpClientAdapter>>>] error: Deserialize error: data did not match any variant of
      untagged enum JsonRpcMessage, when send initialize request
    
    ⚠ MCP startup incomplete (failed: GitLab)
    

    Tools are not available:

    /mcp
    
    🔌  MCP Tools
    
      • GitLab
        • Auth: OAuth
        • Tools: (none)
    
  5. dnsmichi commented on Jun 3, 2026

    @dnsmichi

    I've cloned this repo and asked Codex about this bug report issue, and feedback in https://gitlab.com/gitlab-org/gitlab/-/work_items/561564#note_3415658534 I cannot reproduce the problem in my environment (macOS Tahoe 26.5.1, Codex 0.131.0 - 0.136.0, GitLab.com 19.0 - 19.1. macOS Tahoe 26.5.1, Codex 0.131.0 - 0.136.0, GitLab.com 19.0 - 19.1.).

    It seems that the streamable HTTP messages are directly parsed as JSON-RPC, swallowing other message formats like JSON formatted error messages from the server-side, which could help explain the current behavior in #20009 (comment)

    Here's the full analysis summary with Codex help, and a suggested patch for debugging. I am a beginner with Codex 's code base, so this might not work immediately.

    From the Codex error:

    AuthClient<codex_rmcp_client::http_client_adapter::StreamableHttpClientAdapter>
    ...
    Deserialize error: data did not match any variant of untagged enum JsonRpcMessage,
    when send initialize request
    

    Codex appears to have reached the OAuth-authenticated MCP transport. The failure happens during the streamable HTTP initialize request: Codex expects the response body to be a valid MCP JSON-RPC message, but the response is JSON that does not match JSON-RPC.

    A likely example would be a GitLab/Rails error payload such as: {"message":"404 Not Found"}or another non-MCP JSON error envelope. In that case Codex currently reports a JSON-RPC deserialization failure instead of the underlying HTTP/status/body.

    A useful next step is to capture Codex debug logs:

    mkdir -p /tmp/codex-mcp-debug

    RUST_LOG='codex_rmcp_client=trace,codex_mcp=trace,rmcp=trace,codex_core=debug'
    codex -c log_dir=/tmp/codex-mcp-debug

    Then reproduce the GitLab MCP startup failure and inspect:

    /tmp/codex-mcp-debug/codex-tui.log

    However, Codex may still not log the exact response body before deserialization. If needed, the Codex side should add temporary logging around the HTTP JSON response handling to print status, content-type, and a sanitized body before parsing it as MCP JSON-RPC.

    • diff --git a/codex-rs/rmcp-client/src/http_client_adapter.rs b/codex-rs/rmcp-client/src/http_client_adapter.rs
      index 1234567890..abcdef1234 100644
      --- a/codex-rs/rmcp-client/src/http_client_adapter.rs
      +++ b/codex-rs/rmcp-client/src/http_client_adapter.rs
      @@ -191,8 +191,22 @@ impl StreamableHttpClient for StreamableHttpClientAdapter {
                   }
                   Some(content_type) if content_type.starts_with(JSON_MIME_TYPE) => {
                       let body = collect_body(&mut body_stream).await?;
      -                let message: ServerJsonRpcMessage =
      -                    serde_json::from_slice(&body).map_err(StreamableHttpError::Deserialize)?;
      +                let message: ServerJsonRpcMessage = match serde_json::from_slice(&body) {
      +                    Ok(message) => message,
      +                    Err(error) => {
      +                        tracing::warn!(
      +                            status = response.status,
      +                            content_type,
      +                            session_id = session_id.as_deref().unwrap_or("<none>"),
      +                            mcp_method = mcp_method.as_deref().unwrap_or("<none>"),
      +                            mcp_request_id = mcp_request_id.as_deref().unwrap_or("<none>"),
      +                            has_authorization_header,
      +                            body_preview = %body_preview(String::from_utf8_lossy(&body).to_string()),
      +                            "streamable HTTP JSON response did not deserialize as MCP JSON-RPC"
      +                        );
      +                        return Err(StreamableHttpError::Deserialize(error));
      +                    }
      +                };
                       Ok(StreamableHttpPostResponse::Json(message, session_id))
                   }
                   _ => {

    And re-run with debug logging again.

    This is probably not macOS-specific and not a SQLite/tool-import issue. It looks like GitLab MCP returns a non-JSON-RPC response during initialize, possibly due to 404/prerequisite/tier/Duo/ feature gating or another server-side error path after OAuth.

  6. billypostleBBC commented on Jun 17, 2026

    @billypostleBBC

    Adding another reproduction, this time with Webflow's official remote OAuth MCP server.

    Environment:

    • Codex Doctor: Codex Doctor v0.140.0-alpha.19 · macos-aarch64
    • Runtime from doctor: 0.140.0-alpha.19
    • Codex Desktop / app context
    • macOS: Mac OS 26.5.1 [64-bit]
    • Machine locale: en-GB
    • Server: webflow
    • Transport: streamable_http
    • URL: https://mcp.webflow.com/mcp

    Configured MCP server:

    [mcp_servers.webflow]
    enabled = true
    url = "https://mcp.webflow.com/mcp"

    Observed behavior:

    1. codex mcp list shows Webflow as configured, enabled, and OAuth-backed:
    Name     Url                          Bearer Token Env Var  Status   Auth
    webflow  https://mcp.webflow.com/mcp  -                     enabled  OAuth
    
    1. codex mcp get webflow shows:
    webflow
      enabled: true
      transport: streamable_http
      url: https://mcp.webflow.com/mcp
      bearer_token_env_var: -
      http_headers: -
      env_http_headers: -
    
    1. codex doctor --summary reports MCP configuration as healthy:
    ✓ mcp  4 server (2 stdio, 2 streamable_http) · 0 disabled
    
    1. I then reset auth locally:
    codex mcp logout webflow
    codex mcp login webflow

    The login flow completed successfully:

    Removed OAuth credentials for 'webflow'.
    Successfully logged in to MCP server 'webflow'.
    
    1. After successful re-auth, codex mcp list still shows:
    webflow  https://mcp.webflow.com/mcp  -  enabled  OAuth
    
    1. However, the current/new Codex desktop thread still has no callable Webflow tools. Inspecting local thread tool state:
    select count(*)
    from thread_dynamic_tools
    where lower(coalesce(namespace,'') || ' ' || name || ' ' || description) like '%webflow%';

    returns:

    0
    

    I also rendered a model-visible prompt input locally and searched it for Webflow/MCP tool references. It contains no Webflow tool namespace or Webflow tool instructions, despite the server being configured and re-authenticated.

    Impact:

    • From the user's point of view, Webflow MCP is installed correctly and OAuth succeeds.
    • Codex confirms the server is enabled.
    • doctor confirms MCP config is healthy.
    • But new desktop threads cannot call Webflow at all because the tools are never exposed to the model/thread.

    This looks like the same class of issue described here, but with Webflow's official MCP and a current mcp list output that reports Auth: OAuth rather than Auth: Unsupported.

    Expected behavior:

    • After successful codex mcp login webflow, new Codex desktop threads should expose Webflow MCP tools.
    • If tool import fails, the desktop app/thread should show a specific MCP startup/import error instead of silently omitting the tool namespace.

    Actual behavior:

    • Webflow remains enabled and OAuth-authenticated according to the CLI.
    • No Webflow tools are imported into the thread tool registry.
    • The model has no callable webflow/mcp__webflow__... namespace.
  7. Necmttn commented on Jun 21, 2026

    @Necmttn

    This needs a thread-level MCP import state, separate from auth. For each server, show configured, auth complete, initialize ok, tools/list ok, schema accepted, injected into thread, and model-visible. Include last error at each stage. That would distinguish OAuth success from tool-registry failure without making users manually replay tools/list with the bearer token.


    Generated with ax.

  8. quantum-distortion commented on Aug 31, 2026

    @quantum-distortion

    Adding a fresh Windows/Codex Desktop datapoint and a feature request from a self-hosted remote MCP deployment.

    Environment / target:

    • Product surface tested: Codex Desktop
    • Desktop build observed from the running app environment: 26.820.80927
    • CLI build used for comparison: codex-cli 0.150.0-alpha.8
    • MCP server type: remote streamable_http
    • Auth provider: Cloudflare Access Managed OAuth in front of a self-hosted MCP server
    • Endpoint shape: https://<host>/<redacted>/mcp — path redacted, no secrets included

    Observed behavior:

    • Codex CLI works end to end against the OAuth-protected remote MCP server.
    • CLI dynamic client registration completed successfully.
    • CLI auth status became o_auth.
    • A real read-only MCP tool call completed successfully with isError=false.
    • Unauthenticated public requests to the MCP endpoint are rejected by Cloudflare with 401 application/json and WWW-Authenticate: Bearer ..., as expected.
    • In Codex Desktop, the shared config/auth state showed auth_status: o_auth, but the running Desktop task did not import the remote MCP server's tools into the thread. The OAuth-backed server was therefore not actually usable from the Desktop thread.

    This is a slightly different manifestation than the original auth_status: unsupported report: on this build, auth status can look better (o_auth) while tool injection/model-visible tool availability still fails. From the user's perspective the failure mode is the same: OAuth appears configured, but the Desktop thread cannot call the remote MCP tools.

    Feature request:

    Please make Codex Desktop support remote MCP OAuth with parity to Codex CLI, including DCR + PKCE and ordinary protected-resource metadata flows, and make successful OAuth result in tools being imported into fresh Desktop threads. Ideally, existing/open threads should either refresh their MCP client/tool registry after login/config changes or clearly explain that a restart/new thread is required.

    Also requesting a first-class diagnostic surface that separates these states per MCP server:

    • configured
    • OAuth discovery supported
    • login complete / token present
    • initialize succeeded
    • tools/list succeeded
    • tool schemas accepted
    • tools injected into the current thread
    • tools visible to the model
    • last sanitized error at each stage

    Why this matters:

    The current practical workaround for Codex/GPT Desktop is to put the server behind a static-header or bearer-token path. That can be made narrow and private, but it is materially worse than OAuth for self-hosted/private MCP servers because it requires long-lived client-side secrets. For now, I am retaining a static-header workaround only for GPT/Codex Desktop and using OAuth for clients that pass end to end, but the desired end state is OAuth everywhere rather than static headers in Desktop config.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    authIssues related to authentication and accountsbugSomething isn't workingmcpIssues related to the use of model context protocol (MCP) servers

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions