codex exec silently auto-cancels MCP tool calls regardless of default_tools_approval_mode
When invoking codex exec (non-interactive), MCP tool calls are auto-cancelled with the message user cancelled MCP tool call, even when default_tools_approval_mode = "approve" is set in ~/.codex/config.toml for that MCP. Interactive codex honors the same config and runs the same MCP without issue.
This is surprising because (a) there is no "user" to cancel in non-interactive mode, and (b) the docs imply default_tools_approval_mode controls this exact case.
Reproduce
- Install a plugin with a bundled MCP server (or configure any user-side MCP). Example using an existing plugin:
``bash``
codex plugin marketplace add meridianlabs-ai/inspect-skills
codex plugin add inspect-skills@meridian
- Add the enable block to
~/.codex/config.toml:
``toml``
[plugins."inspect-skills".mcp_servers."py-repl"]
enabled = true
default_tools_approval_mode = "approve"
- Confirm the MCP is registered:
``bash``
codex mcp list
# py-repl uvx ... enabled Unsupported
- Call it via
codex exec:
``bash``
codex exec --sandbox read-only "Use the py-repl MCP. Call execute_python with code='print(2+2)'."
Output:
````
mcp: py-repl/execute_python started
mcp: py-repl/execute_python (failed)
user cancelled MCP tool call
- Run the same prompt in interactive
codex(noexec): the MCP call goes through and returns4.
I verified all three accepted values of default_tools_approval_mode (auto, prompt, approve) — all produce the same user cancelled MCP tool call in codex exec. Dropping --sandbox read-only also doesn't help (the cancellation is not from the sandbox layer).
Expected
codex exec should honor default_tools_approval_mode = "approve" and auto-approve the call, or provide a narrower --auto-approve-mcp style flag for non-interactive use.
Current workaround
codex exec --dangerously-bypass-approvals-and-sandbox lets MCP calls through. But this also disables the sandbox, which is a much bigger blast radius than just auto-approving an explicitly-approved MCP. For automation use cases (CI, scripted runs), a smaller knob would be a much better fit.
Environment
- Codex
v0.139.0(installed via brew) - macOS 26.3.1 (aarch64)
- Plugin sourced from a local marketplace; same behavior expected with git-based marketplaces
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗