Remote-installed plugin MCP prompts `codex mcp login`, but the command cannot resolve the server

Open 💬 4 comments Opened Jul 23, 2026 by Shallow-dusty

What version of Codex CLI is running?

codex-cli 0.145.0 (latest stable at the time of filing)

What subscription/authentication do you have?

ChatGPT authentication

What platform is your computer?

Ubuntu 24.04.3 LTS under WSL2, x86_64

What issue are you seeing?

An account-synced remote plugin can contribute an OAuth MCP server to the running Codex session, and startup then prints an instruction that cannot be executed successfully:

The cloudflare-api MCP server is not logged in.
Run `codex mcp login cloudflare-api`.

MCP startup incomplete (failed: cloudflare-api)

Running the exact suggested command in another shell fails before OAuth starts:

$ codex mcp login cloudflare-api
Error: No MCP server named 'cloudflare-api' found.

The same registry mismatch is visible through the management commands:

$ codex mcp get cloudflare-api --json
Error: No MCP server named 'cloudflare-api' found.

$ codex mcp list --json
# Only locally configured MCP servers are returned.

The user-level ~/.codex/config.toml does not contain a cloudflare-api server or a local Cloudflare plugin entry. The Cloudflare plugin is account-synced and materialized under the remote plugin cache. Its bundled .mcp.json contains:

{
  "mcpServers": {
    "cloudflare-api": {
      "type": "http",
      "url": "https://mcp.cloudflare.com/mcp"
    }
  }
}

The local remote-install marker contains a backend plugin id, confirming that this is remote-installed plugin state rather than a hand-written MCP configuration.

Steps to reproduce

  1. Sign in to Codex with ChatGPT authentication.
  2. Install/enable the OpenAI-curated Cloudflare plugin on a surface that syncs remote plugin installation to the account.
  3. Start Codex CLI on another host or WSL environment using the same account.
  4. Observe that the runtime loads the Cloudflare plugin and reports that cloudflare-api is not logged in.
  5. Run the suggested command:

``bash
codex mcp login cloudflare-api
``

  1. Observe:

``text
Error: No MCP server named 'cloudflare-api' found.
``

Expected behavior

The command printed by the startup warning should work.

More specifically, codex mcp login <name> should resolve the same effective MCP server registry used by the running session, including account-synced remote-installed plugins. If remote plugin MCP authentication must happen through another product surface, the warning should point to that supported flow instead of suggesting a command that cannot resolve the server.

Actual behavior

The running session can see and start the remote plugin MCP server, but a standalone codex mcp login process cannot see the same server name.

This repeats on every startup because the runtime continues to inject the remote plugin, while the suggested login command exits before OAuth.

Workaround

A process-local config override makes the management command resolve the same name and URL without permanently duplicating the MCP server in config.toml:

codex \
  -c 'mcp_servers.cloudflare-api.url="https://mcp.cloudflare.com/mcp"' \
  -c 'mcp_oauth_credentials_store="file"' \
  mcp login cloudflare-api

This workaround was verified end to end on the environment above:

  1. mcp get changed from No MCP server named to a valid streamable_http server record.
  2. The OAuth flow completed with Successfully logged in to MCP server 'cloudflare-api'.
  3. Codex wrote one matching credential entry to ~/.codex/.credentials.json with mode 0600 (token values were not printed).
  4. Fresh management processes resolved the credential in both explicit file mode and default auto mode.
  5. A fresh codex exec process exited successfully without a Cloudflare/MCP startup warning.

The file credential mode is used here because this WSL environment has no Linux Secret Service. It is not required to reproduce the server-registry problem. The important workaround is the one-shot mcp_servers.cloudflare-api.url injection.

This is only a workaround: it relies on duplicating runtime plugin metadata at command invocation. It should not be necessary, and permanently adding the same server to config.toml could create duplicate/conflicting registrations after the bug is fixed.

Source-level evidence

In the 0.145.0 source, run_login:

  1. loads ordinary configuration,
  2. creates a fresh PluginsManager,
  3. immediately calls configured_servers, and
  4. returns No MCP server named... if the requested name is absent.

https://github.com/openai/codex/blob/rust-v0.145.0/codex-rs/cli/src/mcp_cmd.rs#L448-L464

A fresh PluginsManager initializes remote_installed_plugins_cache to None:

https://github.com/openai/codex/blob/rust-v0.145.0/codex-rs/core-plugins/src/manager.rs#L428-L469

When that cache is None, remote installed plugin configs resolve to an empty map:

https://github.com/openai/codex/blob/rust-v0.145.0/codex-rs/core-plugins/src/manager.rs#L749-L759

The long-running plugin/app-server path has separate background tasks that fetch remote installed plugins, populate that cache, and refresh the effective runtime:

https://github.com/openai/codex/blob/rust-v0.145.0/codex-rs/core-plugins/src/manager.rs#L945-L1076

So the startup warning and the standalone management command are resolving MCP servers from differently hydrated plugin state.

The same run_login flow is still present in rust-v0.146.0-alpha.4 and current main at the time of filing.

Related issues checked before filing

  • #20970: plugin-provided Slack MCP is visible to get, but remove cannot act on it. This is the closest registry-management mismatch, but here get, list, and login all miss an account-synced remote plugin that the runtime uses.
  • #26275: account-synced plugin capabilities appear in a remote CLI without local install/config. That establishes the adjacent remote-plugin visibility behavior, but it does not cover the broken login instruction.
  • #30716: successful MCP login is not hot-reloaded into an already-running app. Here the login command cannot begin because it cannot resolve the server.
  • #15122: OAuth login succeeds but does not persist across restarts. Here OAuth cannot start without the temporary registry override.

I could not find an existing issue covering the full combination: runtime-injected remote plugin MCP + startup-generated login instruction + standalone login unable to resolve that server.

View original on GitHub ↗

4 Comments

bbingz · 1 month ago

Analysis (community)

Thanks for the clear repro and the one-shot -c 'mcp_servers…' workaround — that was very helpful for narrowing this down. I traced the reported path on current main (same shape as on rust-v0.145.0 / 0.146.0-alpha.4 at filing) and believe this is a registry split between the long-lived session/app-server plugin path and the short-lived MCP management CLI, not an OAuth bug.

  • Environment / versions (from issue): codex-cli 0.145.0, Ubuntu 24.04 WSL2; ChatGPT auth + account-synced Cloudflare plugin; server name cloudflare-api.
  • Repro status: Confirmed at the source level (I did not re-run the full ChatGPT + remote-plugin E2E here). The issue’s expected vs actual and the config-override workaround line up with the code path below.
  • Observed: Session startup can emit

The cloudflare-api MCP server is not logged in. Run \codex mcp login cloudflare-api\.
then codex mcp login|list|get cloudflare-api fails with
Error: No MCP server named 'cloudflare-api' found.

  • Expected: Management CLI resolves the same effective MCP registry the session used when it suggested login (or at least a clear “remote plugin catalog unavailable” failure, not a hard name-not-found).
  • Workaround (issue): injecting the HTTP server into user config via -c makes mcp login succeed end-to-end — so OAuth itself is fine once the name is in the catalog.

Root-cause hypothesis

Session / app-server hydrates account-synced remote-installed plugins into PluginsManager.remote_installed_plugins_cache (background fetch + bundle materialization). Plugin .mcp.json servers (e.g. cloudflare-api) then enter the session MCP catalog, and connection_manager can suggest codex mcp login <name> when auth is required.

codex mcp login|list|get|logout build a fresh PluginsManager with an empty remote cache and never hydrate it:

  1. cli/src/mcp_cmd.rsrun_login (~L448–L463; same pattern in run_list / run_get / run_logout):

Config::load_with_cli_overridesPluginsManager::newMcpManager::configured_serversbail!("No MCP server named '{name}' found.") when the name is absent.

  1. core/src/mcp.rsMcpManager::configured_servers (~L264–L267) → config.to_mcp_config(plugins_manager).
  2. core/src/config/mod.rsto_mcp_config / to_mcp_config_with_plugin_registrations (~L1563–L1582) → plugins_manager.plugins_for_config.
  3. core-plugins/src/manager.rs:
  • new_with_options sets remote_installed_plugins_cache: None (~L469).
  • plugins_for_config* merges remote plugins only via remote_installed_plugin_configs() (~L596 area).
  • When cache is None, remote_installed_plugin_configs returns {} (~L759–L768) — so plugin MCPs never appear, even if a bundle is already on disk.
  • Network fetch / cache write lives in the remote-installed refresh path (fetch_remote_installed_plugins + write_remote_installed_plugins_cache, loop ~L2410+).
  1. Call sites that do start hydrate/sync (maybe_start_plugin_list_background_tasks_for_config / maybe_start_remote_installed_plugin_bundle_sync) are under app-server plugin list paths — not under mcp_cmd.
  2. codex-mcp/src/connection_manager.rsmcp_init_error_display (~L809) hardcodes the login suggestion without checking whether the CLI management surface can resolve that name.

So this is not a race inside OAuth: the management path never seeds remote_installed_plugins_cache, so the name is missing before OAuth runs. Closest prior threads (#20970, #26275, #30716) touch related plugin-MCP visibility / reload edges but not this full combo (session suggests login → short-lived CLI cannot resolve remote-plugin MCP).

High-level fix outline (not a PR)

Small, focused alignment: make MCP management CLI resolve the same effective MCP registry as the session for remote-installed plugins — without permanently dual-registering them into user config.toml.

  1. Preferred: extract a shared awaitable hydrate helper on PluginsManager (e.g. ensure_remote_installed_plugins_for_config(config, auth)) that, when plugins are enabled and ChatGPT auth is present, synchronously fetch_remote_installed_plugins + write_remote_installed_plugins_cache (and optionally wait/sync materialization for roots already tracked by remote bundle sync), then clear/reload the loaded-plugins cache before configured_servers.
  2. Wire once in the shared helpers used by run_login / run_list / run_get / run_logout (any management entry that uses configured_servers).
  3. Optional secondary: if offline support for already-materialized remotes is desired, a disk fallback that discovers cache roots with remote-install metadata and synthesizes RemoteInstalledPlugin entries when the API is unavailable — not a warning-text-only change.

What not to change (unless product intent says otherwise): OAuth perform_oauth_login path; credential store modes; permanent config.toml dual-registration; connection_manager auth detection (beyond maybe improving the message if hydrate fails).

Risks / non-goals: CLI latency/network on every mcp *; auth-required / offline partial state; accidentally exposing remote plugins when plugins are disabled; concurrent cache semantics are process-local so likely low risk.

Test ideas

Failing-first, in the spirit of docs/contributing.md:

  1. Arrange a temp CODEX_HOME with a materialized remote plugin bundle whose .mcp.json defines mcpServers.cloudflare-api (streamable HTTP URL) + remote-install metadata; plugins enabled; no cloudflare-api in user config.toml.
  2. PluginsManager::new without writing remote_installed_plugins_cache → login-style configured_servers lookup misses the name (documents today’s bug).
  3. After write_remote_installed_plugins_cache (or ensure_remote_installed_… with mocked fetch returning that plugin) → same lookup finds the streamable HTTP server with the expected URL.
  4. If feasible, CLI-level: mcp list --json includes the plugin MCP; mcp get succeeds; mcp login reaches the OAuth entry (mocked transport/auth) rather than “No MCP server named…”.
  5. Regressions: user-config-only MCP still works with empty remote cache; plugins_enabled=false does not invent remote servers; missing ChatGPT auth fails soft (local servers still listed) rather than wiping unrelated config.

Questions for maintainers

Happy to be corrected on any of this — especially product intent:

  1. Should CLI hydrate always hit the network, or is disk-only discovery of already-materialized remote-install markers enough for login/list when offline?
  2. Must mcp login also trigger bundle materialization if the plugin is installed remotely but not yet cached locally (session may have materialized it first)?
  3. Is ChatGPT auth always available in the same process model as CLI mcp login on all platforms?
  4. Should the startup warning remain unconditional, or mention hydrate failure / unsupported surface if the remote list cannot be loaded?
  5. Product intent: are remote-plugin MCPs first-class for all codex mcp * verbs, or login-focused only?

Happy to refine the analysis (or dig into any of the open questions) if useful. If the team later wants an invited PR along an approach you choose, I’d be glad to help — no pressure either way.

bbingz · 1 month ago

Building on the root-cause analysis above with the introducing-commit timeline (which I only described behaviorally as "same pattern in 0.145.0/0.146.0/main") and a verified test gap. Traced at 5dd992a (2026-07-24).

Latent structure - PR #12864. run_login's no-hydrate structure was introduced by PR #12864 (752402c4fe, merged 2026-03-01, "feat: load from plugins"), which touches codex-rs/cli/src/mcp_cmd.rs + core/src/skills/manager.rs + thread_manager.rs. At mcp_cmd.rs it calls mcp_manager.effective_servers(&config, None) (the historical API at the time; later renamed to configured_servers, e.g. by #21356) then bail!("No MCP server named '{name}' found.") - the same no-hydrate pattern is in run_logout and run_get. At that time remote-installed plugins did not exist, so the bug was latent.

Went live - #20096 + #20268. The session-vs-CLI asymmetry was created by two PRs, neither of which touched mcp_cmd.rs. #20096 (73cd831952, 2026-04-29) introduced remote_installed_plugins_cache and the session/app-server refresh paths in codex-rs/core/src/plugins/manager.rs. #20268 (2686873e77, 2026-04-30, "Sync remote installed plugin bundles") added remote bundle sync on app-server startup and plugin/list (core-plugins/.../manager.rs + remote.rs + remote_installed_plugin_sync.rs + app-server/.../plugins.rs + app-server/tests/suite/v2/plugin_list.rs). Neither touched mcp_cmd.rs, so CLI resolution stayed empty-cache while the session path populated the cache.

Not fixed as of 5dd992a. Upstream mcp_cmd.rs's latest commit is 265cd2e / #34995 (2026-07-23, HTTP policy) - unrelated. PR #34877 "Wait for local plugin cache refreshes in plugin/list" (2026-07-23) touches core-plugins/manager.rs + the app-server request_processors/plugins.rs + plugin_list tests + README, not mcp_cmd.rs - so the CLI hydrate gap remains open.

Test gap. The CLI has mcp list/get integration coverage for user-config MCP (cli/tests/mcp_list.rs), but there are no mcp login/logout tests and no coverage of the remote-plugin resolution path with remote_installed_plugins_cache == None. In core-plugins/src/manager_tests.rs, many tests pre-populate the cache via write_remote_installed_plugins_cache(...) then call plugins_for_config (e.g. remote_installed_cache_ignores_plugins_missing_local_cache); none exercise the CLI effective_servers/run_login path, and none assert the bug's defining behavior: cache == None => remote plugin MCP absent despite a materialized bundle on disk. A failing-first test would materialize a remote-plugin bundle, leave remote_installed_plugins_cache == None (no hydrate), and assert the server name is missing from the CLI run_login/effective_servers path - documenting today's gap. To lock a fix, the same setup after a hydrate (write_remote_installed_plugins_cache(...) or fixed CLI wiring) must assert the server name resolves.

Minor line-number drift vs my earlier 0.145.0 citations on main (5dd992a). new_with_options cache=None is now :519 (I said ~L469); remote_installed_plugin_configs returns {} at :809-819 (I said ~L759-768); plugins_for_config merges remote at :646/732/754 (I said ~L596). Drift only; behavior identical.

jovensebido · 11 days ago

Additional reproduction from ChatGPT Pro Work on 2026-08-17:

  • Installed cloudflare@openai-curated-remote, version 0.1.2, from the OpenAI-curated catalog.
  • Canonical plugin dependency metadata reports source_plugin_installed: true and source_plugin_user_enabled: true.
  • The plugin’s Cloudflare skills load successfully.
  • The bundled cloudflare-api MCP tools search() and execute() are not exposed to the Work conversation.
  • Explicitly selecting @Cloudflare in two consecutive turns does not mount either tool.
  • No Cloudflare OAuth or consent screen appears.
  • ChatGPT’s plugin-permission lookup inconsistently reports Cloudflare as not installed even though the plugin dependency record and Plugins interface show it installed.
  • Global Plugins permission was set to Always ask; this did not change MCP tool availability.
  • The Work execution environment does not expose the codex CLI, so the process-local codex mcp login workaround documented in this issue cannot be tested here.
  • No Cloudflare account API was called, no account data was read, and no external resource was mutated.

This appears to extend the effective-registry mismatch to ChatGPT Work: the account-synced plugin bundle and skills are present, but its bundled OAuth MCP server is not available to the conversation’s tool registry.

prod-alissonfgc · 4 days ago

Update: the Cloudflare remote-plugin/MCP integration issue is still reproducible on 2026-08-24.

Environment observed:

  • ChatGPT Plus personal workspace
  • ChatGPT Web and Android app
  • ChatGPT Work
  • OpenAI-curated Cloudflare plugin v0.1.2

Current behavior:

  • Cloudflare plugin installs/enables successfully.
  • Cloudflare skills are available.
  • The bundled cloudflare-api MCP is not exposed to ChatGPT/Work sessions (search() / execute() unavailable).
  • No Cloudflare OAuth consent flow is triggered on first use.
  • Try in chat opens Work, but Work reports that only the plugin skills are available and the official Cloudflare MCP is not available in the session.
  • Uninstalling/reinstalling the plugin does not trigger OAuth.
  • We revoked an older Cloudflare Workers Bindings MCP OAuth authorization on the Cloudflare side before reinstalling; after reinstall there was still no new Cloudflare OAuth authorization created.

This matches the symptom reported in Cloudflare issue cloudflare/mcp-server-cloudflare#446 (“ChatGPT plugin only shows skills. No MCP access.”), which was closed on 2026-08-20 after a maintainer said latest ChatGPT upgrades should have fixed it and mentioned a prior iss auth breakage.

So, at least for this Plus account, the problem persists after that date and across both ChatGPT Web and Android. The Cloudflare plugin appears to materialize skills but not the authenticated MCP app/tooling.