Code support: Claude Code + Codex¶
LazyTools makes the two coding runtimes practical collaborators: delegate a
write, request a review from the other engine, and keep native conversations
under names. The synchronous MCP tools use LazyBridge's CodexEngine
(App Server) and ClaudeCodeEngine (Agent SDK). For long jobs and durable
approval tickets, use the async code bridge.
LazyTools 0.10.2 is Alpha, with lazybridge>=1.8.1,<2.0.
The distribution is lazytoolkit; imports use lazytools.
python -m pip install "lazytoolkit[engines,mcp] @ git+https://github.com/selvaz/LazyTools.git@v0.10.2"
lazytools-mcp projects code_review claude_review code_write code_sessions --allow-unsafe
Sign in to the local runtimes first. With a subscription login they use your
plan's quota; adapters inherit runtime authentication and do not force a
billing path. Configure the code root and project Store
before connecting a host. Coding providers are opt-in even for reviews because
each call runs a model turn. --allow-unsafe exposes writes without interactive
confirmation; it is separate from bridge approval tickets.
Tools to connect the work¶
| Provider | Tools | Purpose |
|---|---|---|
code_review |
codex_code_review, codex_ask, codex_review_changes |
Focused review, design questions, native typed diff review |
claude_review |
claude_code_review, claude_ask |
Focused review and design questions |
code_write |
codex_write, claude_code_write |
File edits and command execution |
code_sessions |
code_sessions_list, code_sessions_bind, code_sessions_rename, code_sessions_forget |
Inspect and manage conversation aliases |
All seven engine-backed review/ask/write tools accept per-call model,
effort and session_name. Codex also accepts thread_id on prompted
review, ask and write; Claude accepts session_id. codex_review_changes
takes scope (uncommitted, branch, commit) and ref, with no prompt slot.
There is no Claude native review-harness counterpart.
Claude writes, Codex reviews¶
The worked bridge flow creates a tiny repository and exercises the job lifecycle. For a short synchronous turn, Python calls the same factories that MCP exposes. Run this from an existing Git repository (the current directory is the code root):
import asyncio
from pathlib import Path
from lazytools.connectors.code_support import CodeWriteTools, codex_reviewer
root = str(Path.cwd())
writer = CodeWriteTools(base_dir=root)
write = next(tool for tool in writer.as_tools() if tool.name == "claude_code_write")
reviewer = codex_reviewer(root=root)
async def collaborate():
writer.confirm_write() # explicitly grant one write call
draft = await write.run(task=
"Add deployment_allowed(day): allow weekdays, block weekends. Add boundary tests.",
cwd=root, model="sonnet", effort="high", session_name="deployment-writer",
)
print(draft)
review = await reviewer.run(
task="Review the uncommitted deployment change and boundary tests; report defects.",
repo_path=root, model="gpt-6.1-sol", effort="high", session_name="deployment-review",
)
print(review)
asyncio.run(collaborate())
This wiring explicitly chooses different engines. MCP does not automatically
prevent arbitrary callers from reviewing with the producing engine. Keep the
diff, findings and independently executed checks before recording
project acceptance.
A review that cannot run raises ReviewNotPerformed in Python and becomes an
MCP error result; it is not a clean review.
Name a native conversation¶
Use session_name="deployment-review" again in the same repository to resume.
Aliases are keyed by kind (codex / claude), resolved repository and name, in
~/.lazybridge/sessions.json or LAZYBRIDGE_SESSIONS_FILE.
Explicit native ids take precedence and rebind a supplied alias.
from pathlib import Path
from lazytools.connectors.code_support import CodeSessionTools
sessions = CodeSessionTools(root=str(Path.cwd()), allow_mutate=True)
print(sessions.code_sessions_list())
sessions.code_sessions_bind("codex", "review", "existing-thread-id")
sessions.code_sessions_rename("codex", "review", "adopted-review")
sessions.code_sessions_forget("codex", "adopted-review")
Replace existing-thread-id with an id from a real reply before using the
bound name for a turn. Binding does not create or validate a native thread;
rename/forget affect only the alias. Claude transcripts and Codex threads
remain engine-specific. Bridge --session names pin subsequent bridge jobs
in that repository to the original engine.
Permissions and timeouts¶
Startup check¶
from lazytools.connectors.code_support import check_clis_available
available = check_clis_available()
print(available) # {"claude": bool, "codex": bool}
This checks executable resolution, not authentication or quota. Claude is
looked up on PATH; Codex uses LazyBridge's resolver, including the desktop
app install directory and CODEX_BIN. Sign in and test the intended engine
before a live task. The engine-backed Claude path also needs the Agent SDK;
raw MCP needs the official MCP SDK. Discovery and a model turn are different
checks: raw MCP tool listing does not prove an authenticated review can run.
Codex reviewers use a read-only sandbox and approval_policy="never".
Claude reviewers get scoped Read/Glob/Grep plus supplied Git tools;
they have no Bash. Reviewers can access the web by default. Writers are a
separate capability: direct CodeWriteTools calls require one-shot grants by
default; the MCP provider disables per-call confirmation. Claude file hooks
confine file tools to cwd; Bash is not path-confined.
CodeWriteTools(claude_bash=False) removes Bash. Codex writes retain the Git
repository check. Write results label repository-derived content as untrusted;
failed writes are not automatically retried.
Calls block until the delegated turn finishes. The default request timeout is
3600 seconds; set the MCP host timeout accordingly (Claude Code's
MCP_TOOL_TIMEOUT uses milliseconds). Prefer detached bridge jobs for long
work, approval queues and independently restartable waiters.
Outer timeout for CLI tools¶
When a CLI wrapper is an LLMEngine tool, use tool_timeout=None or a
strictly larger outer deadline than its subprocess timeout. The synchronous
function runs in a worker thread; cancelling asyncio.wait_for does not stop
that thread or its subprocess. Let the subprocess own its deadline, for example
a 300-second wrapper budget with an outer ceiling greater than 300. Engine
reviewers/writers instead set their request timeout on the factory/provider.
The MCP host must also allow enough time for the call and transport overhead.
Write grants and output contracts¶
confirm_write(task_id=...) can scope a one-shot grant to a task so another
concurrent task cannot spend it. Every invalid cwd is rejected before consuming
a grant. Direct CodeWriteTools defaults to Claude only; set codex=True
deliberately to expose the Codex writer. default_cwd selects the default
subdirectory within base_dir; codex_skip_git_check defaults to False.
CLI wrappers return {"result": ..., "content_is_untrusted": true} on
success, or a [claude_code] ... / [codex] ... error string.
Engine-backed reviewers/consultants return header plus findings/answer and
raise ReviewNotPerformed on a failed turn. Writers return labelled result or
failure text. None of these output shapes is an acceptance verdict.
Other integration surfaces¶
The older CLI wrappers still work: claude_code supports read/plan tasks,
while codex is read-only. claude_code_mcp exposes Claude's raw file/shell tools;
codex_mcp exposes Codex's native codex / codex-reply tools. These use the
MCP connector, require explicit allow= / deny= filters and
differ from the engine-backed LazyTools MCP tools above. See the
Claude guide, Codex guide and
collaboration guide.
Troubleshooting and pitfalls¶
| Check | What it resolves |
|---|---|
check_clis_available() |
Executable lookup; it does not inspect credential files |
Raw MCP alist_tools() |
Installed tool names/schemas and transport connection |
| Factory/provider settings and host timeout | Default 3600-second engine request versus host cancellation |
CodeWriteBlocked reason and selected tool menu |
Missing grant, cwd escape or writer not exposed |
ReviewNotPerformed / MCP isError=True |
Review failed structurally; do not record it as performed |
| Native id/alias and repository | Engine-specific history; ids cannot cross repositories or engines |
Use the engine-specific Codex and Claude tables for recovery details. The raw MCP interfaces evolve with the runtime: discover before relying on a schema. Namespaced filters govern exposed capabilities; a broad deny list can still leave write tools available. Repository text is untrusted input, and scoped parameters do not constrain arbitrary instructions embedded in that text.
See also¶
- Codex for prompted/native reviews, questions, writes and continuation.
- Claude Code for SDK and CLI/raw MCP behavior.
- Collaboration for selecting an integration path.
- Async code bridge for the complete toy-repository flow.
- Projects for shared plans, contracts and verification.
- MCP connector and Safety for transports and gates.