Expose server-level MCP approval defaults in codex mcp add
Summary
mcp_servers.<id>.default_tools_approval_mode is supported in config, but codex mcp add cannot set it. That leaves scripted/noninteractive setups hand-editing TOML, and JSON readbacks do not expose the server-level default.
Proposed change
Add:
codex mcp add <name> --default-tools-approval-mode <auto|prompt|approve> -- <command> [args...]
The flag would persist the existing default_tools_approval_mode field. codex mcp get --json and codex mcp list --json would include it so setup scripts can verify the result. Existing defaults and runtime approval precedence are unchanged.
Ready patch
I prepared and tested a minimal branch:
- Branch: https://github.com/VrtxOmega/codex/tree/codex/mcp-add-default-approval-mode
- Commit: https://github.com/VrtxOmega/codex/commit/80afa2eb37
- Compare: https://github.com/openai/codex/compare/main...VrtxOmega:codex%2Fmcp-add-default-approval-mode
I tried to open this as a pull request, but GitHub shows: "An owner of this repository has limited the ability to open a pull request to users that are collaborators on this repository." Filing this issue so the tested patch is visible and easy to cherry-pick or recreate.
Tests run
PATH=/home/rage/.cargo/bin:$PATH just test -p codex-cli --test mcp_add_remove
PATH=/home/rage/.cargo/bin:$PATH just fix -p codex-cli
git diff --check
PATH=/home/rage/.cargo/bin:$PATH cargo fmt --check -- --config imports_granularity=Item
The targeted CLI integration test passed, including valid persistence/readback and invalid-mode rejection. cargo fmt --check exited 0, with stable rustfmt warning that imports_granularity = Item is nightly-only.
Note: just fmt completed the Rust formatting phase, then failed in the Python SDK ruff phase because openai-codex-cli-bin==0.132.0 has no Linux glibc wheel for this platform. No SDK files were changed.
Related: #24135 and #13476. This is a narrow CLI/config surface improvement, not a complete resolution of all runtime MCP approval behavior.
7 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I would add the flag, but keep the readback clear that this is a server-level default policy, not a persisted approval for any specific tool call.
Three things should stay separate:
For
codex mcp get --json/codex mcp list --json, I would expose both the value and the source:Acceptance tests:
codex mcp addwith default prompt persistsdefault_tools_approval_mode.That keeps scripted setup ergonomic without turning a convenient default into hidden authority for future tool calls.
Thanks, that separation makes sense. I updated the branch to keep the server-level default distinct from per-tool overrides and runtime approvals.
What changed:
codex mcp get --json/codex mcp list --jsonstill includedefault_tools_approval_modefor compatibility.default_tools_approval_mode_sourceandserver_default_tools_approval: { mode, source }, where an unset server default reads back as inheritedauto.tools.<name>withapproval_mode,approval_mode_source, andeffective_approval_mode.codex mcp addpreserves existingmcp_servers.<id>.tools.*overrides when updating command/args or changing/removing the server default.Updated branch: https://github.com/VrtxOmega/codex/tree/codex/mcp-add-default-approval-mode
New commit: https://github.com/VrtxOmega/codex/commit/04c91754e9
Compare: https://github.com/openai/codex/compare/main...VrtxOmega:codex/mcp-add-default-approval-mode
Tests:
PATH=/home/rage/.cargo/bin:$PATH just test -p codex-cli --test mcp_add_remove(13/13 passed)PATH=/home/rage/.cargo/bin:$PATH just fix -p codex-clicd codex-rs && PATH=/home/rage/.cargo/bin:$PATH cargo fmt --check -- --config imports_granularity=Item(passes with stable rustfmt warnings about the nightly-only option)git diff --checkjust fmtstill completes the Rust formatting phase and then hits the existing Python SDK wheel issue foropenai-codex-cli-bin==0.132.0on this Linux glibc platform; no SDK files changed.Nice update. This lands the important split.
The source fields are the part that matters most: scripts can distinguish "this server explicitly defaults to approve" from "this server inherited the normal default." That keeps setup automation ergonomic without turning a config convenience into hidden runtime authority.
One regression I would add before this gets picked up:
--default-tools-approval-mode approve;prompt;auto;prompt;That proves the boundary: server config can narrow or remove the default without erasing explicit tool policy, and runtime approval remains outside persisted config.
Added that regression in
f381bed525:codex mcp add docs --default-tools-approval-mode approve -- docs-server;searchoverride set toprompt;codex mcp add docs -- docs-server-v2, intentionally without the default flag;auto;get --jsonandlist --jsonkeeptools.searchas explicit/effectiveprompt;No runtime approval code changed; this is test coverage for the config/readback boundary.
Updated branch: https://github.com/VrtxOmega/codex/tree/codex/mcp-add-default-approval-mode
Commit: https://github.com/VrtxOmega/codex/commit/f381bed525
Verification:
PATH=/home/rage/.cargo/bin:$PATH just test -p codex-cli --test mcp_add_remove(14/14 passed)cd codex-rs && PATH=/home/rage/.cargo/bin:$PATH cargo fmt --check -- --config imports_granularity=Item(passes with stable rustfmt warnings about the nightly-only option)git diff --checkYes, that regression is the one I would want.
The important thing it proves is the full lifecycle, not just the readback shape: explicit server default -> explicit per-tool override -> server update without the default flag -> inherited server default again, while the tool override stays explicit/effective
promptand runtime approval never leaks into persisted JSON.I would keep the test close to that name and shape. It tells future maintainers this is an authority-boundary regression, not just a CLI serialization case. Nothing else from me.
Thanks, this was genuinely helpful. The authority-boundary framing made the important regression much clearer than the original CLI/config test shape.
I folded that into the branch and will leave it here unless there’s anything else you’d want changed. Appreciate the careful review.