codex mcp add / mcp remove rewrite every [mcp_servers.*] entry, silently dropping comments and unknown keys
Nobody has claimed this yet.
- 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
- 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"
- Run an unrelated command:
$ codex mcp add gamma -- npx -y gamma
Added global MCP server 'gamma'.
- 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 (
enabledmoves 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:
load_global_mcp_serversdeserializes the table intoBTreeMap<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.replace_mcp_serversthen replaces each existing entry with a freshly serialized table (codex-rs/core/src/config/edit.rs:469-479), which discards thetoml_editdecor (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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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