A Rust facade that exposes the Codex app-server thread/turn protocol over Claude Code's bidirectional claude -p transport. Clients send Codex JSON-RPC messages; Claude does the inference and tool execution.
The implementation targets the generated Codex CLI 0.160.0 schema. The Claude transport was developed against Claude Code 2.1.288 and the official Claude Agent SDK's subprocess/control implementation. No Node or Python bridge is required.
This is a working core implementation, not complete Codex feature parity. Unsupported methods return -32601; unsupported non-null options return -32602. See the feature support matrix below for a summary and COMPATIBILITY.md for the exact surface and behavioral differences.
Status: Supported works through the Codex protocol; Partial works with the stated gap; Claude-native is provided by Claude's own runtime rather than the Codex feature; Unsupported is rejected or absent. Details are in COMPATIBILITY.md.
| Area | Feature | Status | Notes |
|---|---|---|---|
| Conversation | Start, resume, list, archive, rename | Supported | Facade-owned records plus native Claude transcripts |
| Conversation | Fork, revert, rollback | Supported | Exact native transcript prefix; files and external effects are not undone |
| Conversation | Steer, queue, interrupt | Supported | One active turn per thread |
| Conversation | Ephemeral threads | Partial | One turn only; cannot be forked |
| Conversation | Model and effort selection | Supported | Claude CLI catalog; low through max |
| Conversation | Context usage, compaction | Supported | Usage from latest top-level request; compaction is Claude's |
| Conversation | Reasoning summaries | Partial | Exposed thinking only; concise/detailed lengths not enforced |
| Conversation | Structured output (outputSchema) |
Supported | Final assistant message containing JSON |
| Conversation | Collaboration modes, plan mode | Unsupported | Default mode only |
| Conversation | Review mode | Unsupported | |
| Subagents | Claude subagents (Agent tool) |
Claude-native | Run inside the turn; output folded into the parent tool result. No Codex child threads, so the desktop agent tree stays empty |
| Subagents | Background agents and workflows | Claude-native | Turn stays open until Claude reports idle |
| Subagents | Codex multi-agent (child thread spawn) | Unsupported | subAgentThreadSpawn lists return no threads |
| Subagents | Guardian/auto approval reviewer | Partial | Mapped to Claude native auto review, not a Codex reviewer |
| Tools | Shell | Supported | Bash → commandExecution; output arrives with the result, not streamed |
| Tools | File edits | Partial | Write/Edit/MultiEdit → fileChange with best-effort diffs; edits made via shell commands produce no file-change item |
| Tools | apply_patch |
Claude-native | Claude edit tools instead |
| Tools | Web search and fetch | Claude-native | Claude WebSearch/WebFetch as dynamicToolCall; no webSearch item |
| Tools | Image viewing | Claude-native | Claude Read on image files; no image-view item |
| Tools | Image generation | Partial | Only through an installed Codex plugin |
| Tools | Plan/todo updates | Supported | TodoWrite → turn/plan/updated |
| Tools | User questions | Supported | Single-select → requestUserInput; multi-select → desktop option picker |
| Tools | Other Claude tools | Partial | dynamicToolCall in namespace claude; generic approval via question fallback |
| Tools | Desktop dynamic tools | Supported | Reverse item/tool/call through the turn owner |
| Tools | Multimodal tool results | Partial | Rendered as text; native MCP result metadata dropped |
| MCP | Claude-configured MCP servers | Claude-native | Shown as mcpToolCall; outside the Bash sandbox |
| MCP | Codex plugin MCP/app tools | Supported | Executed by genuine Codex; metadata preserved |
| MCP | Interactive elicitation | Unsupported | Authenticate in Codex settings |
| Plugins, skills, hooks | Codex plugin administration | Supported | Forwarded to genuine Codex when configured |
| Plugins, skills, hooks | Codex plugin skills | Supported | Listed with real SKILL.md paths |
| Plugins, skills, hooks | Standalone skills, Claude plugins | Claude-native | Loaded by Claude; skill input items unsupported |
| Plugins, skills, hooks | Codex hooks | Partial | Manageable; not executed around Claude turns. Claude hooks run normally |
| Permissions | Workspace-write, full access | Supported | Claude OS-enforced Bash sandbox and scoped file permissions |
| Permissions | Read-only, external sandbox | Unsupported | |
| Permissions | Command/file approvals, session grants | Supported | Session grants match exact tool input and cwd |
| Permissions | never approval policy |
Partial | Maps to dontAsk: denies, does not bypass |
| Input | Text, images | Supported | Data/HTTP(S) URLs, local PNG/JPEG/GIF/WebP |
| Input | Audio, video, file IDs, mentions, skill items | Unsupported | |
| Desktop host | Filesystem, command/exec, process/* |
Supported | Privileged; trusted local clients only |
| Desktop host | Attachments, worktrees | Partial | Metadata stored; desktop Git worker owns checkouts; UI orchestration unverified |
| Desktop host | SSH reconnect | Supported | Live turn survives; facade restart interrupts it |
| Account | Login, quotas, account management | Unsupported | Claude's own authentication |
| Other | Realtime voice, remote control, cloud tasks, transcript import | Unsupported | |
| Platform | macOS / Windows | Supported / Unsupported |
Install Codex desktop and Claude Code, run claude auth login, and enable
System Settings > General > Sharing > Remote Login for your user first.
-
Install the command-line tool:
brew install nicosuave/tap/claudex
-
Set up the desktop connection as your login user, without
sudo:claudex install
This copies the binary to a stable per-user location, creates its LaunchAgent and SSH connection, and checks service readiness. It also enables mixed GPT/Claude models for desktop SSH connections from another computer. Reconnect an existing remote connection after installation.
-
Select
claude-codex-localin Codex Settings > Connections.
To use Claude models beside Codex in the desktop's ordinary local window instead, without SSH, see One local window without SSH.
If you need to diagnose a connection problem, run claudex doctor. It checks
the setup; it does not install or upgrade anything.
See DESKTOP.md for prerequisites, upgrades, service commands, uninstall behavior, and the precise local trust boundary.
Prebuilt macOS (Apple Silicon/Intel) and Linux (ARM64/x86-64) binaries and SHA-256
checksums are on GitHub Releases.
macOS binaries are Developer ID signed and notarized. The desktop installer is
macOS-only; Linux can run claudex app-server --stdio.
Wait for active chats to finish. Update the Homebrew binary, then copy that updated binary into the desktop service and restart it:
brew upgrade nicosuave/tap/claudex
claudex installThe second command refreshes an existing installation and preserves conversations
and the SSH identity. It is currently required because the service runs its own
stable copy of the binary; brew upgrade alone does not update that copy.
Requires Rust with edition 2024 support and an installed, authenticated claude executable. Claude uses its normal configured authentication. The facade neither reads credentials nor changes login settings.
mbx build --locked --bin claude-codex-server
./target/debug/claude-codex-server app-server --stdioUse cargo build if Boxington (mbx) is unavailable. The default model is sonnet; override it with --model opus or a Claude model identifier.
For a client that lets you configure its app-server command, set the executable to the built claude-codex-server binary and arguments to app-server --stdio. For a separate Codex desktop SSH connection, see DESKTOP.md. Desktop transport and startup compatibility are implemented; the actual desktop UI still needs a manual connection test.
Other transports:
./target/debug/claude-codex-server --listen ws://127.0.0.1:4510
./target/debug/claude-codex-server --listen unix:///tmp/claude-codex.sockWebSockets accept loopback IP bindings only. All browser Origin headers are rejected. unix:// carries WebSockets as Codex desktop expects; the previous newline JSON framing is available as unix-lines:///absolute/path. Unix sockets use mode 0600; only stale sockets owned by the current user may be removed; live sockets, files, and symlinks are retained. All transports are intended for trusted local clients, with no network authentication layer. Logs go to stderr; stdout carries only protocol frames in stdio mode.
Useful options:
| Option | Purpose |
|---|---|
--claude /absolute/path/to/claude |
Backend executable; also CLAUDE_CODE_EXECUTABLE |
--state-dir /absolute/path |
Facade persistence; also CLAUDE_CODEX_HOME |
--model sonnet |
Default model alias or identifier |
--claude-arg=--safe-mode |
Forward an administrator-selected argument; repeat as needed |
--initialize-timeout-seconds 60 |
Claude control handshake deadline |
--approval-timeout-seconds 300 |
Deny unanswered permission requests after this much connected time |
--acp-agent opencode |
Serve an Agent Client Protocol agent instead of Claude (see below) |
--acp-arg=acp |
Forward an argument to the ACP agent; repeat as needed |
--provider-id, --provider-name |
Model provider reported to Codex for an ACP agent (default: the executable name) |
--auto-review-model sonnet |
Claude model that reviews an ACP agent's permission requests when a thread selects automatic review |
--idle-session-timeout-seconds 3600 |
Keep a thread's Claude process between turns so its prompt cache stays valid; 0 starts a process per turn |
--max-idle-sessions 8 |
Idle Claude processes kept across threads; the oldest closes first |
State defaults to ~/.local/state/claude-codex-server (claude-codex-server-<provider-id> for an ACP agent), independently of CODEX_HOME. Only one facade process may use a state directory. The facade stores its own thread/item history there, while Claude retains its own native session transcripts. Both are needed for durable conversation recall. Deleting facade metadata does not delete Claude's transcripts.
The same facade can put any Agent Client Protocol v1 agent behind Codex desktop instead of Claude: OpenCode, Gemini CLI, Goose, Claude Code's ACP adapter, and others. Each agent runs as its own facade with its own threads:
claudex app-server --stdio --acp-agent opencode --acp-arg=acp --provider-name OpenCode
claudex app-server --stdio --acp-agent gemini --acp-arg=--experimental-acp --provider-name GeminiFor the desktop, install one connection per agent. Each gets its own LaunchAgent, state, SSH key and host, claude-codex-<provider-id>, next to the Claude connection:
claudex install --acp-agent opencode --acp-arg=acp --provider-name OpenCode
# then select claude-codex-opencode in Codex Settings > Connectionsdoctor, service and uninstall take the same --acp-agent/--provider-id flags to select the instance. Sign in with the agent's own CLI first; a turn that needs authentication fails with the sign-in methods the agent advertises.
The agent runs its own tools, so threads use full access and the agent's permission requests become Codex approvals. Models and reasoning effort come from the agent's session config options, and desktop dynamic tools reach the agent as a stdio MCP server. Fork works only at the latest turn and only for agents that support it; rollback, workspace sandboxing and structured output are refused. Automatic approval review works for ACP agents too: Claude reviews each agent permission request against the Codex guardian policy (--auto-review-model, default sonnet). See COMPATIBILITY.md for the full mapping.
Stdio and unix-lines:// use one JSON object per line. TCP and Unix WebSockets use one JSON object per text frame. As with Codex, outgoing messages omit jsonrpc. String and integer request IDs are preserved.
Send initialize, await its response, then send initialized:
{"id":1,"method":"initialize","params":{"clientInfo":{"name":"my-client","version":"1"},"capabilities":{"experimentalApi":true}}}
{"method":"initialized"}Start a thread, read result.thread.id, then substitute that ID into the turn request:
{"id":2,"method":"thread/start","params":{"model":"sonnet","sandbox":"danger-full-access","approvalPolicy":"on-request"}}
{"id":3,"method":"turn/start","params":{"threadId":"<thread-id>","input":[{"type":"text","text":"Hello","text_elements":[]}]}}The client receives turn/started, item start/delta/completion events, and exactly one turn/completed for an accepted turn. Responses and notifications can interleave. Wait for turn/completed before starting another turn on that thread; separate threads may run concurrently.
A permission request is a server-initiated request with its own ID. For commands and file changes, respond using the exact ID:
{"id":"<approval-request-id>","result":{"decision":"accept"}}The supported decisions are accept, acceptForSession, decline, and cancel. Session grants apply only to identical tool input and cwd within the same thread; broader permission amendments are rejected. Multi-select native questions use the installed desktop option-picker extension. See COMPATIBILITY.md for request and recovery semantics.
Workspace-write uses Claude's OS-enforced Bash sandbox and scoped file permissions; startup verifies the resolved native restrictions before sending user input. Desktop Approve for me selects native Claude auto review. Manual review remains supported. Full access (danger-full-access with never) selects Claude's bypassPermissions; never inside the workspace sandbox selects dontAsk. Eligible unsandboxed retries use the selected reviewer: native automatic review under Approve for me, or desktop approval under manual review. Explicit native ask/deny rules remain authoritative. MCP integrations and hooks remain trusted services outside the Bash sandbox. Read-only/external profiles are rejected. The generated desktop launcher also enables genuine Codex plugin services using the existing plugin registry and authentication; see plugin integration.
mbx test --locked --all-targets --features test-backend
mbx clippy --locked --all-targets --features test-backend -- -D warnings
cargo fmt --all -- --checkThe deterministic suite drives the real facade executable with a fake Claude subprocess. It checks emitted responses, notifications, and approval requests against the vendored upstream schema, plus stream reconciliation, approvals, interruption, background continuations, restart, fork metadata, history pagination, and socket transports. The fake backend is available only with the test-backend feature.
Current verification results and live transport evidence are recorded in the desktop integration audit.
On a heavily loaded host, use RUST_TEST_THREADS=1 TOKIO_WORKER_THREADS=2. If the local mbx test launcher stalls, compile with mbx test --no-run --all-targets --features test-backend, then run the reported test executables directly; this preserves compiler caching.
The live smoke example exercises inference, recall after restarting the facade process, and a fork that remains anchored while the source conversation advances:
mbx build --locked --bin claude-codex-server --example smoke
./target/debug/examples/smoke ./target/debug/claude-codex-serverTo also verify a real command approval, add --exercise-approval. The example approves only the exact command printf FACADE_TOOL_OK, denies other tool requests, and uses a temporary workspace with Claude safe mode. It makes real model calls and leaves native Claude session records in Claude's normal storage.
Live verification status (October 8, 2026): inference, recall after a server restart, fork snapshot isolation, and a real command approval all passed. The smoke example adds an explicit Bash ask rule so its harmless printf command requires a callback. The previous expired-OAuth blocker is resolved. Desktop UI behavior remains unverified because the computer-use tool does not allow access to the Codex app.
transport.rs owns framing and connections; server.rs owns RPC ordering, subscriptions, turns, and approvals; backend.rs owns Claude subprocess/control I/O; translate.rs owns item lifecycles; store.rs owns atomic facade persistence. A completed turn is published only after backend cleanup and process reaping. A Claude background-agent continuation is kept within its initiating Codex turn until Claude reports idle. subagents.rs maps Claude subagents to Codex child threads.
Primary references:
- Codex app-server protocol
- Codex source
- Claude Agent SDK control implementation
- Claude Agent SDK subprocess implementation
The checked-in schema was generated with codex app-server generate-json-schema --experimental from the installed version, rather than fetched from a moving branch. See NOTICE for attribution.
CI runs formatting, Clippy, and deterministic tests on Linux and macOS. See releasing for the tagged Linux workflow, local macOS signing, and Homebrew update process.