MCP bearer-token env var appears configured even when missing from active process
What version of Codex CLI is running?
codex-cli 0.142.0
What platform is your computer?
macOS arm64 / Codex Desktop-bundled CLI
What issue are you seeing?
For a Streamable HTTP MCP server configured with bearer_token_env_var, codex mcp list and codex mcp get can make the server look bearer-authenticated even when the referenced environment variable is not visible to the active Codex process. The actual MCP request then reaches the server without an Authorization header, so protected tools are hidden or startup fails.
This is confusing because the status path says the server has bearer auth configured, while the runtime path cannot send it. codex doctor does notice an MCP warning, but the more direct MCP commands do not surface the missing env var or tell the user that the active Desktop/thread/process needs a restart after the launcher environment is changed.
No token values are included in this report.
What steps can reproduce the bug?
- Configure a remote Streamable HTTP MCP server with a bearer-token env var:
``toml``
[mcp_servers.example]
url = "https://example.com/mcp"
bearer_token_env_var = "EXAMPLE_MCP_TOKEN"
- Launch Codex Desktop or Codex CLI from an environment where
EXAMPLE_MCP_TOKENis not present. - Run
codex mcp listorcodex mcp get example. - Attempt to use the MCP server from the active Codex session.
Observed behavior
In a real repro with a Breakdown MCP server:
$ codex mcp get breakdown
breakdown
enabled: true
transport: streamable_http
url: https://www.breakdown.sh/api/mcp
bearer_token_env_var: BREAKDOWN_API_TOKEN
http_headers: -
env_http_headers: -
$ printenv BREAKDOWN_API_TOKEN >/dev/null && echo set || echo missing
missing
$ codex mcp list
Name Url Bearer Token Env Var Status Auth
breakdown https://www.breakdown.sh/api/mcp BREAKDOWN_API_TOKEN enabled Bearer token
The server-side MCP diagnostic reports that the request arrived without a bearer token:
{
"ok": false,
"state": "missing_token",
"summary": "Breakdown MCP is reachable, but this request did not include a bearer token.",
"error": { "code": "unauthorized", "message": "Missing bearer token" }
}
codex doctor --summary does catch a generic warning:
[!!] mcp MCP configuration has optional issues - Set the missing MCP env vars or disable the affected server.
But the direct codex mcp list/get flow still reads as if bearer auth is ready, and the active MCP tool surface remains unusable until the process/thread is restarted with the env var available.
Expected behavior
Codex should distinguish these states in the MCP status path:
- bearer-token env var is configured
- bearer-token env var is present in the current process environment
- bearer-token env var is missing, so no
Authorizationheader will be sent
Concretely, one or more of these would help:
codex mcp list/getshould show a warning/status such asmissing env var EXAMPLE_MCP_TOKENinstead of onlyAuth: Bearer token.codex doctorshould make the missing server/env-var actionable from the default output, while still redacting secret values.- The active Desktop/thread MCP startup error should say that the configured bearer-token env var is missing from the process environment and that restarting Codex is required after changing GUI/launcher env vars.
- If Codex supports live MCP client/session refresh, changing MCP auth config or logging in should invalidate/recreate the active MCP client; otherwise the UI should explicitly say a new process/thread is required.
Additional information
This may overlap with other MCP auth/session-state reports, but this issue is specifically about bearer_token_env_var being configured while absent from the active process environment. Related issues I found while checking for duplicates:
- #29279: active Desktop thread remains stuck after MCP OAuth login/state changes
- #26760: bearer-token auth failures can suggest the wrong OAuth remediation
- #26072: Streamable HTTP MCP server appears enabled/authenticated but tools are not exposed
4 Comments
I pushed a proposed fix for this here:
https://github.com/openai/codex/compare/main...Nicolas0315:codex:codex/mcp-missing-bearer-env-status?expand=1
Summary:
bearer_token_env_varas unavailable when the named variable is missing, empty, or invalid Unicode in the active Codex process.not_logged_in.codex mcp listtext output and addbearer_token_env_var_statustocodex mcp gettext output.Verification:
just fmtgit diff --checkcargo test --manifest-path codex-rs\Cargo.toml -p codex-rmcp-client determine_auth_status_cargo test --manifest-path codex-rs\Cargo.toml -p codex-mcp mcp_init_error_display_reports_missing_bearer_token_env_varcargo test --manifest-path codex-rs\Cargo.toml -p codex-mcp mcp_startup_failure_reason_requires_existing_oauth_and_auth_failurecargo test --manifest-path codex-rs\Cargo.toml -p codex-cli --test mcp_list list_and_get_report_missing_bearer_token_env_var -- --exactcargo test --manifest-path codex-rs\Cargo.toml -p codex-cli --test mcp_listcargo check --manifest-path codex-rs\Cargo.toml -p codex-cli -p codex-mcp -p codex-rmcp-clientSSH verification across my available machines:
cyberagent-mac-m5max: fresh clone, patch apply,git diff --check, and the targeted rmcp-client/codex-mcp/codex-cli tests passed.nicolas-macbook-pro: fresh clone, patch apply, andgit diff --checkpassed; Rust toolchain is not installed there.rtx5060ti: fresh clone, patch apply, andgit diff --checkpassed; Rust builds are blocked by the host resolvinglinkto GNU coreutils instead of the MSVC linker.nicolas2025: fresh clone, patch apply, andgit diff --checkpassed; cargo/rustc paths on that host are zero-byte stubs.Update: I rebased the branch onto current
origin/main(5267e805f) and force-with-lease updated the fork branch to7950b8a70.Additional verification after the rebase:
just fmt,git diff --check main...HEAD, the targeted rmcp-client/codex-mcp tests, fullcodex-cli --test mcp_list, andcargo check -p codex-cli -p codex-mcp -p codex-rmcp-clientall passed.cyberagent-mac-m5max,nicolas-macbook-pro,rtx5060ti, andnicolas2025against the rebased fork branch.cyberagent-mac-m5max: targeted rmcp-client, codex-mcp, and codex-cli tests passed from a fresh checkout of7950b8a70.Linux Desktop reproduction, because apparently we are testing whether a desktop application can locate a desktop secret store without first completing a shell-environment scavenger hunt.
Environment
26.803.81509-1(x86_64)0.147.0-alpha.6.6Linux 7.1.5-201.fc44.x86_64 x86_64 unknownConfirmed behavior
codex mcp list --jsonreports the server as configured and enabled.bearer_token_env_var./usr/lib/chatgpt/ChatGPTprocess and its bundled Codex child do not contain that variable when launched normally from the desktop entry.initializewithout the credential returns HTTP 401.The current user journey is admirably compact:
Security through existential uncertainty is innovative, but it is not quite secret integration.
Working local workaround
I verified a workaround that:
shell_environment_policy.filtersexclusion so the credential is not propagated into task shells or captured by future shell snapshots.After that, keyring-authenticated MCP initialization returns HTTP 200. So the server, token, protocol, and Linux keyring are all functioning. The missing component is the desktop app doing the desktop-app part.
A desktop application requiring users to invent a keyring-backed launch wrapper for an authentication mode the application already advertises is certainly one interpretation of “integrated.”
What #30125 should fix
The proposed status/error improvement here is necessary: configured bearer auth and usable bearer auth are not the same state, despite their shared enthusiasm for the word “bearer.”
But it does not address the underlying Desktop UX gap tracked by:
For Desktop, the complete behavior should be:
GNOME Secret Service was already running throughout this reproduction and, in a stirring demonstration of professional boundaries, the app never bothered it.
Additional macOS Desktop reproduction:
launchctl getenvThere is also a separate runtime problem: when the bundled CLI is launched directly from a shell where the token is explicitly present, its MCP tool call still fails, while standalone Codex 0.147.0 completes the identical call.
That A/B result is documented separately here: https://github.com/openai/codex/issues/38794
This supports treating Desktop credential resolution and the bundled-runtime MCP failure as two independent defects.