Codex Exec and MCP Approvals: Headless Without the Bypass Flag
Just want to govern Codex? If you're here to actually see, control, and price every Codex tool call, that's one command — hooks for Bash plus an MCP connector for everything else, in a single installer:
curl -sf https://agenticcontrolplane.com/install.sh | bash
Full Codex install guide → · see your first governed call → · free up to 5 agents
Last updated: August 31, 2026.
There’s an open Codex issue — #24135 — that anyone running codex exec in automation eventually finds, usually by way of a failed run. The report is precise: in non-interactive mode, MCP tool calls are auto-cancelled because stdin is closed. The tool call needs an approval; nobody is attached to give one; the call dies. And the reporter’s summary of the only current workaround deserves quoting, because it names the whole problem: there’s no way to allow MCP tool calls non-interactively without --dangerously-bypass-approvals-and-sandbox, and that is “not a viable production posture for a daemon.”
He’s right. It isn’t. The flag OpenAI’s docs label elevated-risk removes the approval gate and the sandbox in one move — for want of a way to answer one question, you take the gate off every question.
Why this is structural, not a bug
An approval prompt is a question addressed to whoever is watching. Headless, by definition, nobody is. Every agent that went headless has hit some version of this and picked a default: Codex cancels the call (deny-shaped, arguably the most defensible), Cursor’s non-interactive mode runs with full write access, OpenHands headless always-approves by design, dsh writes without asking inside the workspace root. People have tried to configure their way out — the Codex tracker shows users inventing plausible config keys like trusted_mcp_servers and mcp_approval_policy that don’t exist — but there’s nothing to configure, because the missing piece isn’t a setting. It’s an answerer.
So the question worth asking isn’t “how do I get the prompt approved in CI.” It’s: why is an unattended run asking questions at all?
An approval prompt exists because the policy is incomplete — the system doesn’t know whether this call is acceptable, so it asks. Interactively, that’s cheap. Headless, incompleteness is the failure. Which means the real fix is to make the policy complete before the run: every call the agent might make should resolve, deterministically, to allow or deny — with “I don’t know” landing on deny-with-a-reason, which the model can see, adapt to, and route around, rather than a cancelled run or a bypassed gate.
The shape that works today
Concretely, for codex exec in CI or on a schedule:
1. Say what you mean with the sandbox. The default is read-only; --sandbox workspace-write for jobs that edit; danger-full-access only inside a container you’d be comfortable losing. (--full-auto is deprecated — details in the hooks reference.)
2. Keep enforcement on the synchronous hook path. Codex keeps PreToolUse firing in sandboxed non-interactive modes — a real differentiator; Claude Code’s equivalent flag turns hooks off. A synchronous hook can deny any call, MCP included on current builds, with a reason that goes back to the model as a tool error. That’s your gate, and it works with stdin closed.
3. Pre-decide what you would have approved. This is the step that replaces the prompt. The calls you’d wave through interactively — the read-only MCP tools, the fetch against your own API, git push to the PR branch — become allow rules. The ones you’d refuse become denies. The residue you can’t classify defaults to deny, and the deny is logged with its reason. A useful source for these rules is your own interactive history: the approvals you’ve already given, session after session, are the policy you haven’t written down yet. (Codex gestures at this itself with approved_with_amendment, which persists a one-off approval into a standing prefix rule — the right instinct, one tool wide.)
4. Keep a record you didn’t author. Headless failures are silent by default — a cancelled MCP call and a policy deny look identical in a green-checked CI log. Whatever enforces your rules should also write the per-call record: what was attempted, what was decided, why. When a run behaves oddly, that log is the difference between a diagnosis and a shrug.
Steps 2–4 are what our Codex integration wires up — the hook, server-side rules evaluated per call, and the audit trail — so read the recommendation with that disclosure attached. But the pattern stands without the product: sandbox pinned, hooks synchronous, policy pre-decided, denies loud, record kept. That’s a daemon posture you can defend.
What it still isn’t is a true mid-run approval — a paused call, a human answering from Slack twenty minutes later, the run resuming. Nothing in the Codex ecosystem does that today (#24135 is open precisely because the plumbing doesn’t exist), and we’d rather say so than imply otherwise. Deterministic policy covers the large majority of what unattended runs actually need; the async-approval residue is real, small, and worth watching the issue for.
Frequently asked questions
Why do MCP tool calls fail in codex exec?
Because they’re waiting for an approval no one can give. In non-interactive mode stdin is closed, so a tool call that would prompt in an interactive session gets auto-cancelled instead. This is tracked in openai/codex#24135, where the reporter notes the only current workaround is –dangerously-bypass-approvals-and-sandbox — which is exactly the flag you don’t want in a daemon.
Is --dangerously-bypass-approvals-and-sandbox safe in CI?
It’s the flag OpenAI’s own docs label elevated-risk, and it removes both the approval gate and the sandbox in one move. If the job touches anything real — credentials, deploy targets, MCP servers with write scopes — you’ve built an unattended agent with no gate and no container. If you must run it, treat the job’s environment as the entire security boundary: minimal credentials, isolated runner, and an independent record of what the agent did.
How do I approve Codex MCP tool calls non-interactively?
Today, you don’t — there is no mid-run approval channel in codex exec, and proposed config keys like mcp_approval_policy don’t exist. What works is removing the need for a mid-run question: pin the sandbox (workspace-write, not full access), keep hooks on the synchronous path so denies still fire headless, and pre-decide the calls you’d have approved — as policy evaluated per call — so ‘unsure’ resolves to a logged deny instead of a cancelled run or a blanket bypass.
Do Codex hooks still run in headless mode?
Yes. Sandboxed non-interactive modes keep PreToolUse and PostToolUse firing — unlike Claude Code, where the skip-permissions flag disables hooks. That’s the seam that makes controlled headless Codex possible: a synchronous hook can still deny a call with a reason the model sees and adapts to, even with no human attached.
Where to read more
- Codex Hooks: The Complete Reference — current hook coverage, deprecations, and the deny contract
- openai/codex#24135 — the canonical issue; watch it for upstream movement
- Which Coding Agents Can You Actually Control, Ranked — how each agent resolves the headless-ask problem
- Codex CLI scout — a runnable headless Codex agent with this posture applied