# Codex Exec and MCP Approvals: Headless Without the Bypass Flag

In non-interactive Codex, MCP tool calls that need approval auto-cancel — stdin is closed, nobody can answer — and the only documented way through is --dangerously-bypass-approvals-and-sandbox. There's a better shape: make the approval question answerable before the run starts.

*Last updated: August 31, 2026.*

There's an open Codex issue — [#24135](https://github.com/openai/codex/issues/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

<img src="/assets/img/screenshots/approvals-history-dark.png" alt="The ACP Approvals page, Approved tab, in our own workspace: 31 approved, 1 rejected, 50 expired. Each card names the tool (Bash.rm), the tier (subagent, unsupervised), the risk tags, the rule that held it, and who answered — one decided from the approval email's one-tap link." width="1440" height="900" loading="lazy" decoding="async" style="width:100%;height:auto;border:1px solid var(--line-2);border-radius:10px;box-shadow:0 20px 50px -24px rgba(0,0,0,0.9);margin:8px 0 6px">
<p style="font-size:13px;color:var(--fg-3);margin:0 0 22px">Approvals raised by unattended runs in our own workspace. The tally is honest about the failure mode too: 50 of 82 expired before anyone answered.</p>

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](/blog/codex-cli-hooks-reference).)

**2. Keep enforcement on the synchronous hook path.** Codex keeps PreToolUse firing in sandboxed non-interactive modes, as Claude Code does under `--dangerously-skip-permissions`. 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](/integrations/codex) 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



## Where to read more

- [Claude Code headless and approvals](/blog/claude-code-headless-approvals) — the sibling page for Claude Code

- [Codex Hooks: The Complete Reference](/blog/codex-cli-hooks-reference) — current hook coverage, deprecations, and the deny contract
- [openai/codex#24135](https://github.com/openai/codex/issues/24135) — the canonical issue; watch it for upstream movement
- [Which Coding Agents Can You Actually Control, Ranked](/blog/coding-agent-control-surfaces-ranked-2026) — how each agent resolves the headless-ask problem
- [Codex CLI scout](https://github.com/agentic-control-plane/acp-governance-sdks/tree/main/examples/framework-scout/codex-cli) — a runnable headless Codex agent with this posture applied
