openai / openai/codex

codex mcp add / mcp remove rewrite every [mcp_servers.*] entry, silently dropping comments and unknown keys

Open
#45,432 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug CLI config mcp
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What happens

codex mcp add rewrites every existing [mcp_servers.*] entry in config.toml, not just the one being added. Anything this build does not model is dropped on the way through, and so is the surrounding formatting.

Environment: codex-cli 0.154.0, Windows 11 (10.0.26200).

Reproduction
  1. Start from a hand-written config:
# TOP COMMENT must survive
model = "gpt-5.6-luna"

# MCP SECTION COMMENT must survive
[mcp_servers.alpha]
command = "npx"                # inline comment A
args = ["-y", "alpha-server"]
startup_timeout_sec = 30
tool_timeout_sec = 120
enabled = false
future_unknown_key = "keep-me"

[mcp_servers.alpha.env]
ALPHA_KEY = "1"

[mcp_servers.alpha.tools.dangerous_tool]
approval_mode = "approve"

[mcp_servers.beta]
url = "https://example.com/mcp"
bearer_token_env_var = "BETA_TOKEN"

[mcp_servers.beta.env_http_headers]
X-Tenant = "TENANT_ENV"
  1. Run an unrelated command:
$ codex mcp add gamma -- npx -y gamma
Added global MCP server 'gamma'.
  1. The resulting config:
# TOP COMMENT must survive
model = "gpt-5.6-luna"

[mcp_servers.alpha]
command = "npx"
args = ["-y", "alpha-server"]
enabled = false
startup_timeout_sec = 30.0
tool_timeout_sec = 120.0

[mcp_servers.alpha.env]
ALPHA_KEY = "1"

[mcp_servers.alpha.tools.dangerous_tool]
approval_mode = "approve"

[mcp_servers.beta]
url = "https://example.com/mcp"
bearer_token_env_var = "BETA_TOKEN"

[mcp_servers.beta.env_http_headers]
X-Tenant = "TENANT_ENV"

[mcp_servers.gamma]
command = "npx"
args = ["-y", "gamma"]
Expected vs actual

Expected: gamma is added; alpha and beta are untouched.

Actual, deleted or altered in the two untouched entries:

  • # MCP SECTION COMMENT must survive - gone.
  • # inline comment A - gone.
  • future_unknown_key = "keep-me" - gone. Any key this build does not recognise is silently removed.
  • startup_timeout_sec = 30 -> 30.0, tool_timeout_sec = 120 -> 120.0.
  • Key order changes (enabled moves above the timeouts).

Values that are modelled survive intact: env, env_http_headers, tools.<name>.approval_mode, bearer_token_env_var, enabled = false.

Root cause

Two halves of the same round-trip:

  1. load_global_mcp_servers deserializes the table into BTreeMap<String, McpServerConfig> (codex-rs/config/src/mcp_edit.rs:19-31). McpServerConfig (codex-rs/config/src/mcp_types.rs:207-279) keeps no representation of unrecognised keys, so they are discarded on load.
  2. replace_mcp_servers then replaces each existing entry with a freshly serialized table (codex-rs/core/src/config/edit.rs:469-479), which discards the toml_edit decor (comments, whitespace, original scalar formatting) attached to that item. Only entries that are new or changed need rewriting; unchanged ones are rewritten anyway.

The same code path is used by codex mcp remove (codex-rs/cli/src/mcp_cmd.rs:487-491) and by codex mcp login, so any of those commands will do this to the whole [mcp_servers] table.

Impact
  • Comments and formatting in [mcp_servers.*] are lost, so annotations such as "disabled because the token rotates weekly" disappear without warning, and the diff for a one-line change is the entire section.
  • Unknown keys are deleted permanently. This bites whenever the config is touched by more than one Codex build - for example a Desktop-bundled CLI and a separately installed npm CLI - because the older build removes keys the newer one wrote.
  • Silent: nothing is printed, and the command exits 0.

Related but distinct: #36844 (config rewrite drops the BOM) is another case of the rewrite not preserving what it did not model.

Possible fix

Edit the toml_edit document in place and only touch the entry being added or removed, leaving all other entries as raw toml::Value instead of round-tripping them through the typed model. If unknown keys must be dropped, doing it explicitly with a warning would at least be visible.

Happy to provide the before/after files and an exact command sequence if that helps.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read codex-rs/config/src/mcp_edit.rs, codex-rs/config/src/mcp_types.rs, and codex-rs/core/src/config/edit.rs around load_global_mcp_servers and replace_mcp_servers. Reproduce the issue with codex mcp add, then check the shared path used by codex mcp remove and codex mcp login. Done means changing one MCP entry leaves other entries' comments, formatting, key order, unknown keys, and values intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.