codex mcp login appears to require dynamic client registration for private OAuth MCP servers; cannot use pre-registered client identity
Description
I’m seeing an interoperability problem between Codex MCP OAuth login and a private remote MCP server that uses FastMCP (Python) with Okta-backed OAuth.
The server is internal/non-public, so I can’t share the real URL or tenant details, but the behavior is consistent and reproducible with a sanitized config.
The key issue seems to be that Codex is attempting Dynamic Client Registration (DCR), while this server/IdP setup works with a pre-registered OAuth client in other MCP clients.
A fixed callback port is not enough to make this work in Codex.
Environment
What version of Codex CLI is running?
codex-cli 0.122.0
What platform is your computer?
macOS 26.4.1 (build 25E253), arm64
Darwin 25.4.0
What issue are you seeing?
With this config:
mcp_oauth_callback_port = 8080
[mcp_servers.private_internal]
url = "https://redacted.example.internal/mcp"
enabled = true
running:
codex mcp login private_internal
fails with:
Error: Registration failed: Dynamic registration failed: Registration failed: HTTP 403 Forbidden: {"errorCode":"E0000005","errorSummary":"Invalid session","errorLink":"E0000005","errorId":"<redacted>","errorCauses":[]}
This server is a private FastMCP-based streamable HTTP MCP server using Okta for OAuth.
The same server works in another MCP client when configured with a pre-registered OAuth client identity and a fixed callback port.
What I expected
One of these should work:
- Codex completes OAuth successfully against this server.
- Codex allows configuring a pre-registered/static OAuth client identity per MCP server.
- Codex emits a clearer error indicating that this server requires a pre-registered client and that Codex currently only supports DCR for this flow.
What actually happened
Codex appears to attempt Dynamic Client Registration and fails before a usable browser auth flow completes.
Changing the callback port to a fixed value (mcp_oauth_callback_port = 8080) did not change the outcome.
Additional context
This looks related to other Codex MCP OAuth issues involving DCR assumptions and nontrivial OAuth servers:
- Slack official MCP: Codex fails with
Dynamic client registration not supported
https://github.com/openai/codex/issues/13200
- Supabase MCP: Codex attempted dynamic registration / generated an authorize URL, but the flow was incompatible with the server’s actual expectations
https://github.com/openai/codex/issues/5254
From the Codex docs, I could find support for:
codex mcp loginmcp_oauth_callback_portmcp_oauth_callback_url- per-server
scopes - per-server
oauth_resource
But I could not find a documented way to provide a per-server static OAuth client id / client identity for MCP login.
Why this matters
There are private enterprise MCP servers that are OAuth-protected but do not behave like public DCR-friendly endpoints. If Codex only supports DCR here, that blocks otherwise valid MCP integrations.
Request
Please clarify whether Codex MCP OAuth login is expected to support:
- private OAuth MCP servers backed by Okta
- pre-registered/static OAuth client identities
- FastMCP-based remote servers that do not support Codex-style DCR assumptions
If not, it would help to either:
- add support for per-server static client configuration, or
- document that limitation explicitly and fail with a more precise error.
10 Comments
I'm experiencing the same trying to connect Codex to the Salesforce hosted MCP servers.
They explicitly outline their reason for not supporting DCR - https://github.com/forcedotcom/mcp-hosted/wiki/Configuring-an-External-Client-App#why-not-dynamic-client-registration
I imagine this will be the approach of other enterprise applications providing MCP servers - so it would be great to see Codex add support for non-DCR authentications - similar to how its available when adding apps to ChatGPT, as well as Claude, Gemini etc.
Hit this same wall today. While we wait for upstream support for pre-registered OAuth clients, there's a working bearer-token workaround that gets Codex talking to
mcp.slack.com:TL;DR: Register your own Slack app, run the OAuth code-exchange manually to mint a user token (
xoxp-…), then configure Codex with--bearer-token-env-var SLACK_MCP_TOKENinstead ofcodex mcp login. Bypasses the DCR step entirely. Slack's hosted MCP server accepts the user token as a bearer once the app has MCP server access enabled.Full step-by-step (including the non-obvious "enable MCP server access on the app" toggle and a cleaned app manifest): https://ianhsiao.me/blog/enabling-slack-mcp-in-codex-and-claude-code-workaround
This has been an issue for us for a while. We have been trying to work around it by putting a FastMCP Ouath proxy in front of our own services where DCR is not supported but it is clunky...
Thanks @madeyexz - I stumbled through the equivalent for Salesforce, and managed to get things working...at least for some testing.
Disclaimer: I would highly recommend against anyone doing the following anywhere other than a dev environment you don't really care about, using raw tokens in env vars is less than ideal!
Also I'm fairly sure the Salesforce token will expire as per the orgs session settings, so probably will need to refresh the token based on that - if its truly a dev/test environment, you can probably just set the session expiry to the maximum 24hrs to minimise how often.
The below is a rehash of @madeyexz 's work (https://ianhsiao.me/blog/enabling-slack-mcp-in-codex-and-claude-code-workaround) - but with the urls etc for Salesforce rather than Slack.
Configure Salesforce
Set up the External App as per https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_mcp_server_eca.htm, but make sure you also enable the
Client Credentials FlowAlso make sure you enabled the MCP servers in the Salesforce org - https://help.salesforce.com/s/articleView?id=platform.api_catalog_activate_salesforce_mcp_servers.htm&type=5
Get bearer token
Note: you can probably achieve the steps below to get the bearer token a bit more easily using the Salesforce Postman etc - but heres the manual way...
Go to the following URL in your browser, substituting your client_id. Also amend scopes as needed:
https://test.salesforce.com/services/oauth2/authorize?response_type=code&client_id={CLIENT_ID}&scope=api+refresh_token+offline_access+sfap_api+mcp_api&redirect_uri=http://localhost:8080/callbackGet the code returned from browser url e.g:
http://localhost:8080/callback?code=<CODE>Call the token endpoint:
Get the bearer token returned e.g:
http://localhost:8080/callback#access_token=<ACCESS_TOKEN>Test it works (substitute the URL for whichever MCP server you are trying to connect to):
If you get a 200, all good.
Add to Codex
Add the bearer token to a shell env or apple keychain (e.g. see @madeyexz 's guide)
Add the MCP servers you wish eg.
codex mcp add salesforce-sobject-all --url https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-all --bearer-token-env-var SF_MCP_TOKENcodex mcp add salesforce-metadata-experts --url https://api.salesforce.com/platform/mcp/v1/sandbox/platform/metadata-experts --bearer-token-env-var SF_MCP_TOKENcodex mcp add salesforce-api-context --url https://api.salesforce.com/platform/mcp/v1/sandbox/platform/salesforce-api-context --bearer-token-env-var SF_MCP_TOKENLaunch codex and you should see the MCP servers starting up, use
/mcpto confirm.Adding below helps
Seems to overcome the issue above, but then the issue still happens with the callback where codex automatically appends
callback_idto the callback, which requires enablingwildcard_redirectto match codex pattern<your url>:port/callback/:callback_idAdding a concrete official-hosted MCP repro for the same class of problem: Meta Ads MCP at
https://mcp.facebook.com/adsfails during dynamic client registration before any browser/OAuth consent flow opens.I opened a separate issue with the exact command, config, and error here: #24103
The error is:
This is not a private/internal OAuth server case; it is Meta's official hosted Ads MCP endpoint.
I investigated this further and have a focused implementation prepared in a fork.
Summary of the approach:
mcp_oauth_callback_urlvalues verbatim as registered redirect URIs.httpcallback URLs must use localhost or a loopback IP with an explicit matching listener port, while public HTTPS registered redirect URLs can still usemcp_oauth_callback_portfor the local listener.Local validation:
just fmtjust fix -p codex-rmcp-clientcargo build -p codex-cliCARGO_NET_GIT_FETCH_WITH_CLI=true just test -p codex-rmcp-clientFork PR for reference:
https://github.com/Ahmed-aleryani/codex/pull/1
Would a maintainer be open to an upstream PR for this approach?
cc @gpeal because you previously handled related MCP OAuth protected-resource discovery work in #5254 / #5423. No pressure if this belongs with someone else.
This looks related to #30460 from the redirect-uri side of the same static/pre-registered OAuth client story.
For providers that require pre-registered clients, exact redirect URI matching is often enforced. Codex has
mcp_oauth_callback_url, but currently mutates the configured value by appending/<callback_id>, so a client registered forhttp://localhost:<port>/callbackcan still fail with a redirect URI mismatch.#30460 is intentionally narrower than the full pre-registered-client support request: it proposes treating explicit
mcp_oauth_callback_urlvalues as exact redirect URIs, while preserving callback-id behavior for generated local redirects.Adding a concrete official-hosted Snowflake Managed MCP repro for the same pre-registered/static OAuth client problem.
Environment:
codex-cli 0.142.4Working endpoint shape, redacted:
Repro:
Observed:
I verified the Snowflake endpoint itself is advertising OAuth correctly. A request to the MCP URL returns
401with aWWW-Authenticate: Bearer resource_metadata=...challenge, and the protected resource metadata includes an authorization server and scopes, for example:Snowflake's own Managed MCP docs explicitly say the Snowflake-managed MCP server supports OAuth 2.0 but does not support Dynamic Client Registration. It expects an OAuth security integration / pre-registered client instead: Snowflake Managed MCP OAuth setup.
So this is not a private/internal-only case: Snowflake Managed MCP is an official hosted MCP server with OAuth discovery, but Codex currently attempts DCR and fails before any browser consent flow can proceed.
Codex CLI currently exposes
--oauth-client-idoncodex mcp add, but I could not find a documented way to provide the corresponding OAuth client secret / pre-registered client credentials for this flow. Claude Code documents--client-id,--client-secret, and--callback-portfor exactly this non-DCR case, so the missing piece appears to be first-class pre-registered OAuth client support in Codex MCP login.Expected behavior:
Workaround: PAT bearer auth may be possible, but Snowflake recommends OAuth over hardcoded tokens, and PAT is weaker operationally for this use case.
Links / references:
One more data point from the Snowflake Managed MCP case: this DCR-vs-pre-registered-client mismatch is not unique to Codex. The same Snowflake endpoint also causes DCR-oriented MCP onboarding to fail in other agent CLIs.
In my local check, OpenCode
opencode mcp add/ OAuth debug initially failed withIncompatible auth server: does not support dynamic client registrationagainst the same Snowflake Managed MCP server. OpenCode became usable only after manually configuring a pre-registered OAuth client (clientId,clientSecret,redirectUri) in config and then running its MCP auth flow.There are also community reports around Claude Code + Snowflake Managed MCP hitting the same class of issue, though recent Claude Code docs expose pre-registered OAuth client options.
So I think the compatibility bar here should be: Codex should not assume DCR is available for remote OAuth MCP servers. It should provide a first-class pre-registered OAuth client path, or at minimum fail with a very explicit message saying the server requires pre-registration and Codex does not support that flow yet.
Links / references:
clientId,clientSecret,redirectUri