DEV Community

Iliya Oblakov
Iliya Oblakov

Posted on

Read-only Codex starts your MCP servers too. Disable them before the review.

A codex exec -s read-only review can't write files, and the MCP servers in your ~/.codex/config.toml start for it anyway. A server that can change things outside the sandbox works against a read-only review, so the Codex notes in my review gate say to set enabled = false on that server while the review runs.

Hooks come along too. A Stop hook that asks Codex to continue after its review can replace the final message with an empty one, and an empty final message leaves the gate with no verdict to save.

I listed both in my setup lessons post. This post follows one review through the gate and puts each fix next to what the official Codex docs say.

One review, in order

My gate calls Codex like this:

codex exec -s read-only --color never --output-last-message <file> -
Enter fullscreen mode Exit fullscreen mode

Around that call, in order:

  1. review.sh deletes any verdict already saved for the commit, so a failed rerun can't leave an old ACCEPT behind.
  2. It sends review-prompt.md, the commits and the full diff from the base to HEAD to Codex on stdin.
  3. Without REVIEW_MODEL, Codex takes the model and reasoning effort from ~/.codex/config.toml, and the MCP servers configured there start too.
  4. The sandbox lets Codex read files and run read-only commands such as git diff. It can't write.
  5. Codex writes the review. Hooks set up for Codex run under codex exec as well, a Stop hook included.
  6. --output-last-message saves the final message to a file, and review.sh reads the verdict from that file only.
  7. merge-check.sh refuses any commit that has no saved verdict.

The official non-interactive docs (checked 10 Nov 2026) cover the flags: codex exec runs in a read-only sandbox by default, --sandbox workspace-write allows edits, -o is the short form of --output-last-message, and codex exec - takes the prompt from stdin. The two leaks come in at steps 3 and 5.

MCP servers from config.toml start for the review

The official MCP page says Codex keeps MCP configuration in config.toml, by default ~/.codex/config.toml, and that a trusted project can scope its own servers in .codex/config.toml. Before a review, go through both files for servers that can change things outside the sandbox and switch each one off:

[mcp_servers.<name>]
enabled = false
Enter fullscreen mode Exit fullscreen mode

The page describes enabled as optional: "Set false to disable a server without deleting it." The entry stays in the file, so switching the server back on after the review is a one-line change.

The official page documents the switch. It doesn't say, on its own, that a read-only codex exec starts MCP servers. That behaviour comes from the Codex notes in my gate, the same notes the setup lessons post draws on.

Three more settings from the same docs affect a review:

  1. required = true makes startup fail "if this enabled server isn't ready". The non-interactive page adds that when such a server fails to initialize, codex exec exits with an error instead of continuing without it. My gate treats any reviewer exit other than 0 as REVIEW FAILED and saves no verdict, so a required server that won't start costs you the review.
  2. --ignore-user-config is for "a run that doesn't load $CODEX_HOME/config.toml". My gate's call doesn't pass it, and the gate takes the model and reasoning effort from ~/.codex/config.toml unless REVIEW_MODEL is set. The docs describe the flag in terms of that one file, and project-scoped servers sit in the repo's .codex/config.toml, so check the repo under review as well.
  3. enabled_tools and disabled_tools are tool allow and deny lists for a single server. The fix in my gate notes turns off the whole server.

To see which MCP tools a Codex run calls, try the review prompt once with codex exec --json outside the gate. Stdout becomes a JSON Lines stream of the events Codex emits, and the documented item types include MCP tool calls.

A Stop hook can empty the final message

Hooks set up for Codex also run under codex exec. A Stop hook that asks Codex to continue after its review can replace the final message with an empty one.

My gate reads the verdict from the final message and never searches the log. The prompt Codex gets contains Verdict: ACCEPT as the example of the format, so a search of anything that repeats the prompt can find an ACCEPT the reviewer never gave. An empty final message leaves nothing to read.

On 23 October I ran the no-verdict cases in a scratch repo with my gate's scripts. The empty answer came from a small fake codex I wrote for the test, and review.sh printed:

NO VERDICT: codex wrote no final message
Enter fullscreen mode Exit fullscreen mode

It exited 1 and saved no verdict. A Stop hook ends the review on the same line, with the diagnostic log kept and nothing saved. merge-check.sh then stops with "no review verdict for ; run review.sh", so the commit can't pass and the review you waited for says nothing.

The gate notes give two ways out:

  1. Switch the hook off for reviews.
  2. Add a line to review-prompt.md asking Codex to repeat its complete review if anything asks it to continue.

The no-verdict post covers the other ways a review ends without a verdict: a crash, a cut-off answer and two Verdict lines.

When Claude Code is the reviewer

BUILDER=codex swaps the roles, and my gate calls Claude Code like this:

claude -p "Do the review described on standard input." --restricted --strict-mcp-config \
  --permission-mode dontAsk --tools "Read,Grep,Glob" --allowedTools "Read,Grep,Glob" \
  --disallowedTools "Bash,Edit,MultiEdit,Write,NotebookEdit,WebFetch,WebSearch,Task,Agent"
Enter fullscreen mode Exit fullscreen mode

--strict-mcp-config with no MCP configuration given means no MCP servers load. Read, Grep and Glob are the only tools, so Claude Code can't run commands, git included, and can't edit or write files. That's why review.sh puts the commits and the diff into the prompt. It also refuses to start when claude --help doesn't list --restricted.

The Claude Code review shuts MCP out with a flag in the call, while the Codex review starts whatever servers config.toml leaves enabled.

The writer and reviewer post walks through the swap.

If you ship an MCP connector, get it reviewed

The enabled = false switch is for MCP servers you run. If you ship an MCP connector, sort your own tools the same way, by whether they change data: readOnlyHint: true on every read-only tool, destructiveHint: true on every tool that modifies or deletes data, and read and write tools kept separate.

Those items are on Anthropic's connector pre-submission checklist. My MCP connector review is an independent review of an existing MCP connector against that checklist, before it goes to customers or the Claude directory. Every tool goes through the MCP Inspector, and through Claude as a custom connector where hosting allows it. You get:

  1. A checklist report.
  2. Tests of every tool with valid input, bad input, denied access and an upstream error.
  3. A two-user permission boundary check.
  4. Each finding with severity, impact, reproduction steps and a proposed fix.
  5. One retest after you fix the findings.

Anthropic doesn't run, approve or certify this review, and the review can't guarantee a Community or Verified listing. If the server isn't built yet, the MCP build puts one over your existing API.

The Two Engines Kit is a set of files for working with Claude Code and Codex the way this post does, and I've been building it since the first Codex and Claude CLIs.

Top comments (0)