claude-in-codex
Ask Claude Code for an independent code review or second opinion, straight from Codex.
claude-in-codex is review-only: Claude reviews, critiques, and advises. It does not edit
your code, run shell commands, or get write tools. It is the mirror image of
openai/codex-plugin-cc, which lets Claude call
Codex.
What it does
- Lets Codex call the Claude Code CLI through MCP.
- Sends Claude bounded context for reviews, critiques, and second opinions.
- Returns structured findings Codex can summarize or act on.
- Keeps Codex as the actor and Claude as a read-only reviewer.
Quickstart
You need:
- Codex
- the
claudeCLI, installed and authenticated - Python 3.11 or newer
uvxgit
Check the basics:
claude --version
claude /login
uvx --version
git --version
Add this repository as a Codex marketplace, then install the plugin from it:
codex plugin marketplace add briandconnelly/claude-in-codex
codex plugin add claude-in-codex
Restart Codex after installing. Then ask Codex:
Ask Claude to run
claude_status.
claude_status is free. It checks whether the claude CLI is installed, authenticated, and
compatible, gives a ready stop/continue signal, and shows the defaults a paid call would use.
Use it
Once claude_status reports ready: true, ask Codex in plain language:
Ask Claude to review my current diff. Pass
workspace_rootas this repository.
Passing workspace_root matters. It keeps reviews pointed at your project instead of the
plugin install directory when the MCP client does not provide a repo root.
Other useful prompts:
- "Ask Claude to review my staged changes for security issues."
- "Have Claude attack this plan for weaknesses."
- "Get an independent second opinion from Claude on this design."
- "Start a background Claude review of this branch against main."
- "Ask Claude in the background whether this schema migration is safe."
Use this when you want a second model to look for bugs, regressions, missing tests, security issues, or weak assumptions before you merge or commit to a design.
Codex uses the plugin skill to choose the right tool and arguments. Direct MCP calls are also available:
| Tool | Use | Cost |
|---|---|---|
claude_review_changes |
Review a git diff now | paid |
claude_review_changes_async |
Start a background diff review | paid |
claude_adversarial_review |
Pressure-test a plan, claim, or change | paid |
claude_adversarial_review_async |
Start a background adversarial review | paid |
claude_consult |
Ask for a free-form second opinion | paid |
claude_consult_async |
Start a background second opinion | paid |
claude_status |
Check readiness and defaults | free |
claude_dry_run |
Preview diff/context before a review | free |
Diff review scopes are working_tree, staged, and branch.
Review staged changes with a test-coverage focus:
claude_review_changes({
"workspace_root": "/absolute/path/to/your/repo",
"scope": "staged",
"focus": "tests"
})
For a long review, launch it in the background:
claude_review_changes_async({
"workspace_root": "/absolute/path/to/your/repo",
"scope": "branch",
"base": "main"
})
Branch the reply on outcome, which every successful launch carries:
started— a new paid job is running. Poll withclaude_job_status, then fetch the result withclaude_job_result.existing_job— anidempotency_keyreplay handed back a job that already existed, so it may already be finished. The reply is a job status: read itsstatusandresult_available, and go straight toclaude_job_resultwhen it is already done.no_changes— the diff was empty, so the spend was skipped and the result is inline; there is no job to poll.
Branch on outcome, not on whether job_id is present: both started and existing_job
carry one, so its presence cannot tell a fresh launch from a replay. The full routing table is
published under claude_capabilities.async_lifecycle.start_outcome_routing.
Each of the three paid operations — claude_consult, claude_review_changes, and
claude_adversarial_review — has an _async form. No paid tool is blocking-only.
Prefer the _async form whenever the run may outlive the call: a blocking call that is
cancelled or loses its connection loses the work it already paid for, while a job keeps
running and claude_job_result still pays out. Pass idempotency_key so a retry after a
dropped connection replays the existing job instead of starting a second paid one.
Background runs are bounded by the job deadline (CLAUDE_IN_CODEX_JOB_MAX_SECONDS), not by
timeout_seconds, which the _async tools therefore do not accept.
Safety and cost
- Paid tools run Claude and send code or prompts to Anthropic, billed through your existing
claudelogin orANTHROPIC_API_KEY. - Free tools only inspect local state, preflight a request, or manage background jobs.
- Claude never receives write or Bash tools from this plugin. Claude Code hooks are not
tools and may run in
config_mode=inherit/scoped; useconfig_mode=safeorconfig_mode=barefor untrusted workspaces. access=toollessis the default: Claude receives gathered context as text and cannot read more files.access=readonlylets Claude useRead,Grep, andGlobfor extra context.- Secret redaction is best-effort defense in depth. Use
access=toollesswhen a workspace may contain secrets. - Explicit
max_budget_usdvalues must be between$0.01and$5.00inclusive and are rejected outside that range. It is a best-effort Claude CLI stop threshold, not a hard cap. Results distinguish the requested/configured threshold from the effective value and report actual spend inmeta.cost_usdwhen available. - Reviews default to
effort=xhighfor depth. Lowerefforttohighormediumfor routine reviews when cost matters. system_prompt_append(onclaude_consult,claude_consult_async,claude_review_changes, andclaude_review_changes_async) adds your own persona or focus directive to Claude's system prompt. The plugin's guardrail prompt always leads and cannot be replaced, and your text is bracketed by markers whose closing side restates that the guardrails outrank anything between them. It is capped at 4096 bytes and rejected before any spend. Text containing one of those marker lines, or a near-miss with a common ASCII fence orcaller suppliedunhyphenated, is refused. That makes it harder to forge a close and pose as server-authored instructions; a determined caller can reword a marker past the pattern, which is why the guardrails also tell Claude to distrust anything between the markers.meta.system_prompt_appendrecords a SHA-256 and byte length of the text, never the text, so a result shows it ran under a non-default prompt — on sync results and on background-job results alike. A background job stores that same fingerprint on disk, never your text — though Claude's reply is stored until consumed or expired, and a reply can repeat anything you sent. Neitherclaude_adversarial_reviewform accepts it.- One guarantee there is mechanical and one is not. Claude cannot gain a tool from this text:
the allowlist rides the command line, not the prompt. Claude is only instructed not to let
the text dictate a verdict. Treat
system_prompt_appendas a trust boundary you are widening — it is the one way caller text reaches the system turn — and never build it from untrusted workspace content. Note that the system prompt rides the command line, so your text is visible to a process listing on that machine for the duration of the run. focus(onclaude_review_changesandclaude_review_changes_async) narrows a review to a topic. Your text stays in the user turn, and the server no longer restates it in its own voice: it is delimited by markers of its own, announced as caller-supplied and untrusted, and followed by a sentence saying it may not limit the review's scope, remove a finding, relax a rule, or set the verdict. The guardrail prompt namesfocusalongside the diff as untrusted data. It is capped at 4096 bytes and rejected before any spend, and, like the append, text carrying one of the server's framing marker lines is refused — each channel reserves both families, so neither can forge the other's delimiters. The framing makes an injected directive visible as caller text; it does not make Claude incapable of following one, so never buildfocusfrom untrusted workspace content.pathsnarrows which changes the server gathers. Its values are never interpolated into server-authored prose: the prompt says only that a caller-supplied filter was applied, and keeps the caveat thataccess=readonlymay still permit reads outside the filter. Path validation cannot police this channel — spaces, punctuation, and prose are legal in filenames — so the fix is that the values do not reach the server's own voice, where an injected sentence would have read as task framing. The guarantee is the voice, not the bytes: an entry naming a file the diff contains still appears in that file's diff header, as untrusted diff data. The guardrail prompt also names path filters as untrusted. What none of this covers is omission: a filter chosen from untrusted material can hide the very changes worth reviewing before Claude ever sees them, so treatpathsas scope you decide, and never derive it blindly from workspace content.
If a requested diff scope has no changes, the review tools return a passing result without invoking Claude.
Mental model
Codex remains responsible for deciding what to do with Claude's feedback. Claude receives only
the context the plugin provides, or read-only file access when you explicitly allow
access=readonly. The plugin does not give Claude write tools, Bash tools, or permission to
modify your workspace. In inherit and scoped, Claude Code may still load workspace hooks
from .claude/settings*.json; those hooks execute outside the tool allowlist.
Common knobs
Every setting is optional. These are the knobs most users are likely to change:
| Variable | Default | Purpose |
|---|---|---|
CLAUDE_IN_CODEX_ACCESS |
toolless |
toolless or readonly |
CLAUDE_IN_CODEX_CLAUDE_CONFIG |
inherit |
inherit, scoped, safe, or bare |
CLAUDE_IN_CODEX_EFFORT |
xhigh |
low, medium, high, xhigh, or max |
CLAUDE_IN_CODEX_MAX_BUDGET_USD |
1.00 |
best-effort per-call budget threshold |
CLAUDE_IN_CODEX_MODEL |
unset | Claude model; unset uses the CLI default |
CLAUDE_IN_CODEX_TIMEOUT_SECONDS |
180 |
per-call timeout, clamped to 10-600 seconds |
ANTHROPIC_API_KEY |
unset | required only for config_mode=bare |
Set these in the environment you launch Codex from. The bundled MCP config forwards the common cost, safety, model, timeout, and API-key variables to the server.
config_mode=inherit uses your normal Claude environment without persisting a session.
scoped drops user-global settings and user MCP servers but keeps CLAUDE.md and workspace
hooks. safe disables Claude Code customizations and hooks while preserving normal
authentication. bare strips CLAUDE.md, memory, and hooks, and requires
ANTHROPIC_API_KEY.
Troubleshooting
Start with:
Ask Claude to run
claude_status.
Then:
- If
claude_authenticatedis false, runclaude /login. - If
readyis false, inspectreadiness_detailanddefault_errorsbefore making paid calls. - If the workspace looks wrong, pass
workspace_rootexplicitly. - If a review is large or expensive, run
claude_dry_runfirst. - If a background job id is lost, use
claude_job_list. - If
config_mode=barefails, confirmANTHROPIC_API_KEYis set in the environment that launches Codex.
Distribution
The Codex plugin install path is the primary user-facing path. The bundled MCP config pins the server to a versioned Git tag so installed users update deliberately.
The Python package publishes the MCP server entry point for direct use and release provenance. After a PyPI release, the server can also be launched with:
uvx --from claude-in-codex==0.10.0 claude-in-codex-mcp
Advanced reference
Every tool returns a structured ok/error envelope. Paid results include usage metadata and
cost when the Claude CLI reports it. The tool contract is experimental and pre-1.0; clients can
pin meta.fingerprint to detect agent-visible changes.
The Claude CLI compatibility assumptions are centralized in
src/claude_in_codex/cli_contract.py and documented
in COMPATIBILITY.md.
Local development
Run the MCP server from a checkout:
codex mcp add claude-in-codex -- uv run --directory "$(pwd)" claude-in-codex-mcp
Run tests:
uv run pytest
The full suite enforces a 95% coverage floor. For one-file iteration:
uv run pytest tests/test_jobs.py --no-cov
Live Claude integration tests are excluded by default:
uv run pytest -m integration --no-cov
They require the claude CLI and make a small paid API call.
Metadata
Release files for claude-in-codex 0.10.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| claude_in_codex-0.10.0.tar.gz | 665.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| claude_in_codex-0.10.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 819.6 kB
Release files / claude_in_codex-0.10.0.tar.gz
| Download URL | claude_in_codex-0.10.0.tar.gz |
|---|---|
| Size | 665.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
977d01b6f8de44e7d40a468c6a5a321f18e5fcfdf81319d541efaca6e3735099
|
|
BLAKE2b-256 checksum How to use checksums |
326c748c853fb86c7fd7d3f9ff220a7408e53e31629a9d58eaf9084e06022121
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / claude_in_codex-0.10.0-py3-none-any.whl
| Download URL | claude_in_codex-0.10.0-py3-none-any.whl |
|---|---|
| Size | 154.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4996f5abbbe30057d4d2a35e38c588dcc68d8ea9b993b8ed3e0689dae8bec3bb
|
|
BLAKE2b-256 checksum How to use checksums |
464b298959ff35a7a42f3fe72a7e5eb23664c57924c5bd28810cc7c40aec03cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency log