Codex MCP Sidecar
by 0Pinky0
README.md
# Codex MCP Sidecar
Codex MCP Sidecar is a local MCP server for letting Claude Code start, resume,
and wait for Codex `app-server` sessions while a human can inspect those live
sessions from a terminal.
It is designed for a specific collaboration pattern:
1. Claude starts Codex through MCP and gets back a small run handle.
2. The Codex turn keeps running in the background by default.
3. A human opens `codex-runs`, selects the active run, and attaches to the same
live Codex session.
4. Claude calls `codex_wait` until the final result is ready.
This project is a local sidecar around the Codex CLI. It is not an official
OpenAI product.
## Why
When Claude Code calls another agent through a normal MCP tool, the human often
sees only one opaque tool-call placeholder. That makes supervision difficult,
especially for long running Codex work.
This sidecar keeps the Claude-facing MCP response compact, but stores the full
audit trail under the project working directory and exposes a local picker for
human inspection.
## Features
- Uses Codex `app-server` instead of `codex exec` for Claude-facing calls.
- Defaults `codex_run` and `codex_resume` to async mode so live turns remain
inspectable.
- Provides `codex_wait` with a 240 second soft wait slice by default, helping
Claude keep prompt cache warm during long Codex work.
- Stores audit artifacts in `<project-root>/.codex/codex-mcp/runs/`.
- Maintains a small user-level run pointer index in
`$CODEX_HOME/mcp-wrapper/runs.index.jsonl`.
- Lets humans inspect active and completed runs through the `codex-runs` CLI.
- Keeps MCP tool payloads small and leaves detailed audit data on disk.
- Avoids exposing direct inspect commands, backend details, file paths, command
logs, parse errors, or deep links in normal MCP responses.
## Requirements
- Node.js 20, 22, or 24 (the CI-supported versions).
- npm.
- Codex CLI available as `codex`.
- A Codex CLI build that supports `codex app-server` and
`codex resume --remote`.
- Claude Code or another MCP client that can run stdio MCP servers.
## Installation
Clone the repository and install dependencies:
```bash
git clone git@github.com:0Pinky0/codex-mcp-sidecar.git
cd codex-mcp-sidecar
npm ci
```
Optional local bin setup:
```bash
npm link
```
That exposes:
- `codex-mcp-sidecar`: stdio MCP server entrypoint.
- `codex-mcp-wrapper`: compatibility alias for the same server.
- `codex-runs`: terminal picker for inspecting Codex runs.
You can also run the server directly with `node <repo-path>/server.mjs`.
## Claude Code Configuration
Register the server with Claude Code. The MCP server name can be `codex` if you
want Claude to see this sidecar as the main Codex integration:
```bash
claude mcp add-json codex '{
"type": "stdio",
"command": "node",
"args": ["<repo-path>/server.mjs"],
"env": {
"CODEX_MCP_WRAPPER_APP_SERVER_MODE": "private"
}
}' --scope user
```
Replace `<repo-path>` with the absolute path to this repository.
Dependencies are installed with `npm ci` from the checked-in lockfile. CI runs
`npm audit --omit=dev --audit-level=moderate`; transitive findings fail the
build and must be reviewed before a release. Use the npm registry (or an
approved internal proxy) rather than an unreviewed package mirror.
After registration, call `codex_wrapper_check` from Claude. A healthy local
configuration returns `ok: true` and the detected Codex CLI version.
## Exposed MCP Tools
### `codex_run`
Starts a new persisted Codex app-server turn.
Important defaults:
- `async: true`
- `sandbox: "read-only"` and `approval_policy: "never"`
- app-server mode: `private` (stdio; shared WebSocket is opt-in)
- audit directory: `<cwd>/.codex/codex-mcp/runs/`
Example:
```json
{
"prompt": "Audit the current diff. Return findings first, then residual risk.",
"run_name": "diff-audit",
"cwd": "<project-root>",
"approval_policy": "never",
"timeout_ms": 1800000
}
```
Async calls return a compact running handle, typically including `runId`,
`status`, `threadId`, and timeout state.
### `codex_resume`
Resumes a previous Codex session by wrapper `run_id`.
The caller does not need to know the internal Codex thread id. The sidecar
resolves it from the active run metadata, the project run index, the user-level
pointer index, or compatible old audit artifacts.
Example:
```json
{
"run_id": "<previous wrapper run id>",
"prompt": "Continue from the previous audit and focus on test coverage.",
"cwd": "<project-root>",
"timeout_ms": 1800000
}
```
### `codex_wait`
Waits for an async `codex_run` or `codex_resume` turn.
By default, `codex_wait` soft-returns after 240 seconds if Codex is still
working. The Codex turn is not stopped. Claude should call `codex_wait` again
with the same `run_id`.
Example:
```json
{
"run_id": "<run id from codex_run or codex_resume>",
"timeout_ms": 1800000,
"soft_timeout_ms": 240000
}
```
A soft timeout response looks like this:
```json
{
"runId": "<same run id>",
"status": "still_running",
"threadId": "<codex thread id>",
"timedOut": false,
"waitElapsedMs": 240000,
"nextWaitMs": 240000,
"progress": {
"events": 12,
"totalTokens": 120,
"outputTokens": 20,
"reasoningTokens": 5
}
}
```
## Codex App Server migration
App Server is the primary integration surface. The legacy `codex mcp-server`
command is deprecated by the Codex CLI; new Claude Code installations should
use the official Codex plugin when the sidecar's audit and human-inspection
workflow is not needed. This sidecar intentionally keeps its own `codex_*`
tools and asynchronous run handles, so it is not a drop-in replacement for the
legacy `codex`/`codex-reply` MCP tools. Any compatibility adapter is optional.
The wire contract is tracked against the upstream [Codex App Server
README](https://github.com/openai/codex/blob/main/codex-rs/app-server/README.md)
and the upstream [Codex MCP interface](https://github.com/openai/codex/blob/main/codex-rs/docs/codex_mcp_interface.md).
To opt in to the versioned adapter, set `CODEX_MCP_SIDECAR_COMPAT=1` before
starting the MCP server. It exposes the official `codex` and `codex-reply`
names and returns both `structuredContent` (`threadId` plus string `content`)
and legacy text content blocks. Official field spelling is preserved, including
`approval-policy`, `base-instructions`, `developer-instructions`, and
`compact-prompt`; `codex_home` is a sidecar-only extension for resolving a
thread across project homes. The adapter intentionally accepts only
`approval-policy: "never"`; interactive `on-request` calls are rejected rather
than silently downgraded.
Use absolute paths for `CODEX_HOME`, `cwd`, and `add_dirs` when overriding the
defaults. Tool arguments also accept `~` shorthand and the sidecar resolves it
before validation; MCP clients do not generally expand a literal `~` in an
environment value, so omitting `CODEX_HOME` or providing an absolute path is
the most portable setup.
Image inputs follow the current App Server contract: use an inline `data:` URL
or a local filesystem path. Remote `http://` and `https://` image URLs are
rejected before a turn starts.
Set `soft_timeout_ms` to `0` only when the caller deliberately wants one long
blocking wait.
### `codex_active_runs`
Lists currently active app-server turns from the audit directory. The response
is intentionally compact and does not include inspect commands or artifact
paths.
### `codex_runs`
Lists recent wrapper run handles without calling Codex.
Returned items are limited to:
```json
{
"runId": "<wrapper run id>",
"status": "completed",
"title": "Readable title"
}
```
### `codex_wrapper_check`
Checks the local Codex binary, Codex home, run directory, pointer index, default
timeouts, and detected Codex CLI version.
## Human Inspection
The MCP tools do not return direct inspect commands. Humans inspect locally with
the terminal picker:
```bash
cd <project-root>
codex-runs
```
The picker shows two columns:
- `Running`: active background Codex turns.
- `Completed`: recent completed turns.
Use arrow keys to move, PageUp/PageDown to change pages, Enter to attach, and
`q`, Esc, or Ctrl-C to quit.
When a shared run is selected, the picker attaches using the underlying Codex
command:
```bash
codex resume --remote ws://127.0.0.1:45123 <threadId>
```
For a private stdio run, it uses `codex resume <threadId>` against the selected
workspace and persisted Codex home. A live private process is not exposed on a
network socket; the raw JSONL and active metadata remain available while it
runs.
After the attached Codex CLI exits, the picker returns to the main list instead
of closing.
Other useful modes:
```bash
codex-runs --all # Read the user-level pointer index across projects.
codex-runs --json # Print machine-readable session data.
codex-runs --no-watch # Disable automatic refresh.
```
## Audit Artifacts
For each run, the sidecar writes audit files under:
```text
<project-root>/.codex/codex-mcp/runs/
```
Typical files:
- `<runId>.jsonl`: raw Codex event stream.
- `<runId>.stderr.log`: Codex stderr.
- `<runId>.md`: human-readable audit summary.
- `<runId>.result.json`: atomic terminal result used for restart recovery.
- `index.jsonl`: append-only project run index.
- `active/<runId>.json`: active run metadata while Codex is still working.
The sidecar also writes a small pointer index under:
```text
$CODEX_HOME/mcp-wrapper/runs.index.jsonl
```
That pointer index is used to resolve `codex_resume(run_id)` across projects.
## Security Notes
The default policy is `sandbox=read-only` with `approval_policy=never` and no
interactive callbacks. Full access (`sandbox=danger-full-access` or an explicit
permission profile) must be selected by the caller and is recorded in the run
metadata. Interactive approval policies are rejected by this non-interactive
sidecar; use a callback-capable official integration when approvals are needed.
Recommended precautions:
- Run the sidecar only on a machine and workspace you trust.
- Do not expose the shared app-server WebSocket to untrusted networks.
- Treat `.codex/codex-mcp/runs/` as audit data that may contain prompts,
command output, file paths, and model responses.
- Review audit artifacts before sharing them.
Pass `sandbox` explicitly when a broader or narrower Codex sandbox is needed.
`permissions` and `sandbox` are mutually exclusive; named profiles are checked
with App Server's `permissionProfile/list` method before a turn starts.
When `permissions` or `add_dirs` is supplied, the sidecar negotiates Codex's
experimental capability specifically for those implemented fields so absolute
runtime roots and profile identifiers can be sent; ordinary runs keep the
stable capability surface.
## Environment Variables
The environment variable names still use the historical
`CODEX_MCP_WRAPPER_*` prefix for compatibility.
- `CODEX_BIN`: Codex binary. Defaults to `codex`.
- `CODEX_MCP_WRAPPER_CODEX_HOME`: Codex home used by the sidecar. Defaults to
`CODEX_HOME` or `~/.codex`.
- `CODEX_MCP_WRAPPER_RUN_DIR`: audit directory override. Defaults to
`<cwd>/.codex/codex-mcp/runs`.
- `CODEX_MCP_WRAPPER_POINTER_DIR`: user-level pointer index directory. Defaults
to `$CODEX_HOME/mcp-wrapper`.
- `CODEX_MCP_WRAPPER_TIMEOUT_MS`: default hard timeout. Defaults to 30 minutes.
- `CODEX_MCP_WRAPPER_WAIT_SOFT_TIMEOUT_MS`: default soft wait slice. Defaults
to `240000`. Set to `0` to disable soft returns.
- `CODEX_MCP_WRAPPER_APP_SERVER_MODE`: `shared` or `private`. Defaults to
`private` (stdio). Shared mode also accepts `unix://` (the Codex home control
socket) or an absolute `unix://` endpoint; both use the Unix WebSocket
Upgrade transport rather than raw JSONL.
- `CODEX_MCP_WRAPPER_APP_SERVER_URL`: shared app-server endpoint. Loopback
`ws://`, authenticated non-loopback `wss://`, and Codex Unix control sockets
(`unix://` or an absolute `unix://` path) are supported.
- `CODEX_MCP_WRAPPER_APP_SERVER_AUTH_TOKEN_ENV`: name of an environment
variable holding an out-of-band bearer token for non-loopback `wss://`.
- `CODEX_MCP_WRAPPER_APP_SERVER_LISTEN`: compatibility alias for
`CODEX_MCP_WRAPPER_APP_SERVER_URL`.
- `CODEX_MCP_WRAPPER_DEFAULT_PERMISSIONS`: default app-server permissions
profile. Defaults to none (read-only sandbox is used).
- `CODEX_MCP_WRAPPER_SUPPORTED_CODEX_RANGE`: optional controlled-deployment
override for the tested CLI semver interval. The default is
`>=0.151.0 <0.152.0`; unknown or out-of-range versions fail closed.
- `CODEX_MCP_SIDECAR_COMPAT`: set to `1` to opt in to versioned `codex` and
`codex-reply` compatibility tools; disabled by default.
## Development
Install dependencies:
```bash
npm ci
```
Run tests and syntax checks:
```bash
npm run ci
```
CI installs `@openai/codex@0.151.0` from npmjs.org on each Node 20/22/24
job so the initialize/thread-start/thread-resume and stable/experimental schema
contracts do not silently skip when Codex is absent.
Run the local configuration smoke check:
```bash
npm run check-config
```
`npm run check-config` depends on a local Codex CLI installation and is not part
of the GitHub Actions workflow.
## Repository Hygiene
The repository intentionally ignores local state:
- `node_modules/`
- `.codex/`
- `runs/`
- `.serena/`
- editor folders and environment files
Keep audit artifacts out of commits unless a sanitized fixture is deliberately
added for a test.
## License
UNLICENSED.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues