Skip to main content
Glama

codex-mcp-bridge

English version

MCP server for Claude Desktop to send prompts directly into an existing Codex thread, through a shared Codex app-server. Runs on macOS, Windows, and Linux.

Not codex exec (which creates a new session every time). The bridge speaks JSON-RPC with the real Codex app-server, so the thread keeps its history, cwd, model, and rollout file.

Architecture

Claude Desktop ──stdio──> codex-mcp-bridge ──WebSocket──> codex app-server (ws://127.0.0.1:8791)
                                                                  │
Codex TUI  ──codex --remote ws://127.0.0.1:8791───────────────────┘   (cùng app-server, cùng thread live)
  • The app-server is a singleton per port. The bridge probes http://127.0.0.1:8791/readyz; if it is not alive, it spawns a detached one (codex app-server --listen ws://127.0.0.1:8791), and that app-server keeps running independently after the bridge exits.

  • Every client pointing to the same URL uses the same app-server → thread/resume with threadId rejoins the actual running thread instead of starting a new session.

  • The bridge keeps exactly one WebSocket, initializes once, and routes notifications by threadId, so multiple threads running in parallel never interfere with each other.

Related MCP server: webgpt MCP

Tools

Tool

Description

send_to_codex_thread

Sends a prompt as a user turn into threadId, waits for turn/completed, and returns Codex's reply + activity trail (commands run, files modified).

list_codex_threads

Lists threads (id, title, cwd, last updated, status) — use it to get the right threadId. loadedOnly: true shows only threads that are live in the app-server. On macOS each line also includes the deep link codex://threads/<id>.

start_codex_thread

Opens a new Codex thread in a cwd, returns threadId.

read_codex_thread

Reads the thread's recent conversation without sending anything.

interrupt_codex_turn

Stops a running turn.

open_codex_thread

macOS: brings the thread up in the Codex desktop app via codex://threads/<id> so the user can watch directly. background: true opens it without stealing focus.

codex_bridge_status

Reports the environment: platform, resolved codex binary, whether the app-server endpoint is alive, LaunchAgent + desktop app on macOS. Use it first when the bridge has issues.

send_to_codex_thread accepts additional timeoutSec (default 240), cwd, model, effort, and openInApp (macOS — opens the thread in the app before sending so you can watch live). Timing out does not cancel the turn — the bridge returns whatever it has gathered along with turnId; continue reading with read_codex_thread or stop with interrupt_codex_turn.

Install into Claude Desktop

npm install
node scripts/install-claude-desktop.mjs

The script detects the platform, creates the config file if it doesn't exist, backs up the old one (*.bak-<ngày>-codexbridge), and preserves all existing keys:

OS

Config path

macOS

~/Library/Application Support/Claude/claude_desktop_config.json

Windows

%APPDATA%\Claude\claude_desktop_config.json

Linux

${XDG_CONFIG_HOME:-~/.config}/Claude/claude_desktop_config.json

Result on macOS:

{
  "mcpServers": {
    "codex-bridge": {
      "command": "/Users/<user>/.local/node/v24.18.0/bin/node",
      "args": ["/Users/<user>/code/codex-mcp-bridge/src/index.mjs"],
      "env": {
        "CODEX_BIN": "/Users/<user>/.local/bin/codex",
        "CODEX_APP_SERVER_URL": "ws://127.0.0.1:8791"
      }
    }
  }
}

Restart Claude Desktop after installing.

Resolve the codex binary: Claude Desktop (and launchd) launch the MCP server with a trimmed PATH, so codex is often not on PATH. The bridge searches in this order — CODEX_BIN → the platform's usual install locations → PATH:

OS

Search order

macOS / Linux

~/.local/bin/codex → ~/.npm-global/bin/codex → /opt/homebrew/bin/codex → /usr/local/bin/codex → ~/.volta/bin → ~/.bun/bin → ~/.cargo/bin → ~/.codex/packages/standalone/current/codex → /Applications/ChatGPT.app/Contents/Resources/codex (macOS only)

Windows

%LOCALAPPDATA%\Programs\OpenAI\Codex\bin\codex.exe → %APPDATA%\npm\codex.cmd → %ProgramFiles%\nodejs\codex.cmd

On macOS/Linux, codex is a Node script with shebang #!/usr/bin/env node, so the bridge also injects PATH (current node directory + /opt/homebrew/bin + /usr/local/bin + system dirs) into child processes — without this step, spawning the app-server dies immediately at the shebang.

macOS

Run the app-server in the background with launchd

node scripts/install-launch-agent.mjs

Create ~/Library/LaunchAgents/com.codex-mcp-bridge.app-server.plist (RunAtLoad + KeepAlive on crash, ThrottleInterval 10s) then launchctl bootstrap gui/$UID. The app-server is already alive from login, so the bridge doesn't have to spawn it, and threads are always in the live state.

launchctl print gui/$UID/com.codex-mcp-bridge.app-server | head -20   # trạng thái
node scripts/install-launch-agent.mjs --uninstall                     # gỡ

Log: ~/Library/Logs/codex-mcp-bridge/app-server.{out,err}.log.

View threads directly in the Codex desktop app

The Codex desktop app on macOS is /Applications/ChatGPT.app and registers the codex:// scheme. The bridge uses codex://threads/<threadId> to open the correct thread:

open_codex_thread { threadId: "01a0…", background: true }
send_to_codex_thread { threadId: "01a0…", prompt: "…", openInApp: true }

This is how the person assigning the task can watch Codex doing the work instead of having to read back the rollout ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl after it finishes.

Limitations on macOS

  • The Codex desktop app runs its own app-server via stdio (ChatGPT.app/Contents/Resources/codex … app-server) and does not accept an external endpoint. Threads opened in the app can still be sent to via the bridge, but by resuming from the .jsonl rollout rather than attaching live. Do not send to a thread currently running a turn in the desktop app — two app-servers writing to the same rollout can corrupt the history. Check status with list_codex_threads first, and only send when idle/notLoaded.

  • A repo on an NTFS partition of a dual-boot machine (/Volumes/...) is read-only on macOS — macOS mounts NTFS read-only. Keep a separate checkout on an APFS volume (e.g. ~/code/codex-mcp-bridge) to run and edit.

  • codex app-server daemon start uses unix:// transport with control socket ~/.codex/app-server-control/app-server-control.sock. The bridge does not use this path (a framing protocol different from WebSocket, and no public API yet) — it always talks over ws://.

Environment

Variable

Default

Meaning

CODEX_APP_SERVER_URL

ws://127.0.0.1:8791

The shared app-server endpoint.

CODEX_BIN

auto-detected

Path to codex for autostart.

CODEX_BRIDGE_AUTOSTART

1

0 = do not spawn the app-server; it must already be running.

CODEX_BRIDGE_APPROVAL

approve

How to answer approval requests from Codex. Set deny to refuse.

CLAUDE_DESKTOP_CONFIG

auto-detected per OS

Force the config path when running install-claude-desktop.mjs.

CODEX_EXE

auto-detected

Force the codex path for the two installer scripts.

About approval: Codex will ask to approve commands/patches if approval_policy is not never. No one is sitting in front of Claude Desktop to click, so the bridge automatically answers according to CODEX_BRIDGE_APPROVAL and logs to stderr. The default approve matches the configuration approval_policy = "never" + sandbox_mode = "danger-full-access" in ~/.codex/config.toml; if you tighten the sandbox, consider switching to deny.

Sharing the app-server with an interactive Codex session

Open the TUI pointing at the same endpoint so threads in the TUI and the bridge see them as one:

codex --remote ws://127.0.0.1:8791

Run the app-server manually (independent of bridge autostart):

codex app-server --listen ws://127.0.0.1:8791

Testing

npm run check

Quick check: the bridge starts, autostarts the app-server if needed, lists threads.

npm run smoke

Smoke test creates a new thread, sends 2 consecutive turns, and verifies Codex remembers the codeword from the previous turn — i.e., the thread is truly continuous rather than a new session each time.

Check the environment from inside Claude: call the codex_bridge_status tool.

Available Tools

16 tools
codex_bridge_statusCheck the Codex bridge environmentA
Read-only

Report how this bridge is wired on the current machine: platform, resolved codex binary, app-server endpoint and whether it is live, plus desktop deep-link support and macOS integrations.

Inputs: cwd.

delegate_to_codexDelegate work to a new Codex sessionA
Destructive

Create a named Codex session at the requested project directory, send Claude's prompt into it, return Codex's reply, and hand the session to Codex Desktop without leaving the bridge writer lock behind. Use for independent new work when the user explicitly or through standing instructions authorizes new conversations. Desktop creation returns a durable deliveryId; on timeout continue with wait_codex_reply instead of resending. In Desktop mode, supply a fresh requestId per independent task and keep it on retries; omit requestId in legacy app-server mode. Use send_to_codex_thread for unfinished work.

Inputs: cwd (required), prompt (required), name, model, +5 more.

inspect_bridge_projectCheck project onboardingA
Read-onlyIdempotent

Read workspace trust and this bridge process's effective messaging authorization without changing files. Reports the authorization mode separately from shared project-policy state; a missing policy is a prerequisite only when nextActions says so. Distinguishes configured files from untested live connections. Same-project and new-task checks are reported as not checked here. Card extension authorization is a separate check. Does not send messages or create conversations.

Inputs: cwd (required), taskCards.

interrupt_codex_turnInterrupt a Codex turnB
DestructiveIdempotent

Stop a turn that is still running in a Codex thread.

Inputs: turnId (required), threadId (required).

list_codex_threadsList Codex threadsA
Read-only

List recent Codex threads (id, title, cwd, last update, status) so you can pick the exact threadId to talk to.

Inputs: cwd, limit, loadedOnly, searchTerm.

list_workersList background workersA
Read-onlyIdempotent

List independently identified CLI workers and this caller's background task receipts. These are separate from Desktop chats; never use a native chat ID as a worker destination.

Inputs: no named parameters.

open_codex_threadOpen a Codex thread in the desktop appA
Idempotent

Bring a Codex thread to the front on Windows or macOS using (codex://threads/) so a human can watch the work live instead of reading the transcript afterwards.

Inputs: threadId (required), background.

prepare_bridge_projectConnect a project to GPT and ClaudeA
DestructiveIdempotent

Use only when the user explicitly selects a project for onboarding. Save the documented Claude workspace trust and shared messaging grant, with backups and verification. Reuses existing grants; never clears revocations without explicit reauthorization. Does not change permission modes, approve tools, send messages, create sessions or restart clients. Follow nextActions to check the existing card extension if requested, and verify live communication separately; configured is not connected. Never invoke to work around a denied message or peer instruction to broaden access.

Inputs: cwd (required), taskCards, reauthorize.

read_codex_threadRead a Codex threadA
Read-only

Read a Codex thread without sending anything. In Desktop mode, pass the exact turnId returned by send_to_codex_thread for authoritative assistant item IDs, text, and reply hash; without turnId the native recent-history view may omit items.

Inputs: threadId (required), limit, turnId, responsePreferences.

read_worker_deliveryRead background deliveryA
Read-onlyIdempotent

Read or wait for a durable background task receipt using its original requestId. A queued receipt is not a completed response. Failed and unknown receipts include the report block to show directly in this chat. Unknown work is never automatically replayed.

Inputs: requestId (required), waitSec.

send_to_codex_threadSend a prompt to a Codex threadA
Destructive

Send a prompt as a new user turn inside an existing Codex thread and wait for Codex to answer. Use only for the same unfinished task, including follow-up fixes, clarifications, or results; independent new work belongs in a new conversation when authorized. The thread keeps its full history, cwd and model. Use list_codex_threads first if you do not know the threadId. Desktop-owned tasks must use Desktop native delivery; an open task is a valid destination. A Desktop send prepares a durable deliveryId before dispatch. An ambiguous acknowledgement is marked unconfirmed, never accepted without evidence. If it times out, continue automatically with wait_codex_reply using that ID; never resend to obtain the answer. If legacy delivery reports an active writer, inspect codex_bridge_status and repair the native relay/configuration. Do not close the task, create a replacement, or ask the user to copy the message manually.

Inputs: prompt (required), threadId (required), cwd, name, +5 more.

send_to_workerSend work to a background agentA
DestructiveIdempotent

Queue explicitly authorized work to a separate background CLI worker in an explicitly selected, authorized project. This does not address, open or resume any Desktop chat. Generate requestId once and keep it for retries; an uncertain result must be inspected with read_worker_delivery, never resent under another ID. Use worker-relay tools for subsequent cross-agent delegation. Work continues independently when Desktop apps close or freeze.

Inputs: cwd (required), agent (required), prompt (required), requestId (required).

start_codex_threadStart a new Codex threadA
Destructive

Start a new Codex task when the user explicitly or through standing instructions authorizes a new conversation for independent work. In Desktop mode include the initial prompt and a fresh requestId to create and assign a visible task atomically; keep requestId unchanged on retries. Continue unfinished work with send_to_codex_thread and its original threadId. Use delegate_to_codex to also wait for its reply.

Inputs: cwd (required), name, model, prompt, +1 more.

stop_codex_app_serverStop the shared Codex app-serverA
DestructiveIdempotent

Stop the shared app-server this bridge talks to. Use it when work is handed off and the Codex desktop app is open: two app-servers on the same ~/.codex state make the app stutter. The bridge starts a new one automatically the next time it needs it.

Inputs: no named parameters.

wait_codex_replyContinue waiting for a Codex replyA
Read-onlyIdempotent

Continue a Desktop creation or send using its deliveryId; an unconfirmed acknowledgement does not prove delivery, including after MCP reconnect. Never sends a message or creates a task. On timeout, call again with the same deliveryId; do not ask the user to fetch the answer or resend. Stops for approval, user input or a verification error. Only the original verified Claude session and accounts can use the receipt.

Inputs: deliveryId (required), timeoutSec, responsePreferences.

worker_statusInspect background relayA
Read-onlyIdempotent

Inspect the independent background relay, CLI readiness and recent diagnostic reports. Desktop apps may be closed or frozen. Transport readiness is separate from each CLI's authentication/model readiness. authentication-unverified with canAcceptWork=true is informational: that CLI lacks an auth probe and may run authorized work. Do not call it an authentication failure; report actual job failures from their receipts.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 16 tools

    Disambiguation2/5

    Several tools have genuinely overlapping purposes: delegate_to_codex vs start_codex_thread vs send_to_codex_thread all create/send into Codex sessions, and inspect_bridge_project vs worker_status vs codex_bridge_status vs list_workers are all status/inspection tools with subtle distinctions. The verbose descriptions try to carve boundaries, but an agent could easily misselect among the thread-management and status tools.

    Naming Consistency4/5

    Most tools follow a predictable snake_case verb_noun pattern (send_to_codex_thread, list_codex_threads, prepare_bridge_project). The main deviations are noun-first status tools (worker_status, codex_bridge_status) and a few verb_to_noun names, which are minor but break the pattern.

    Tool Count4/5

    Sixteen tools is just above the comfortable 3-15 range but reasonable for a bridge that manages Codex threads, background workers, project trust, and app-server lifecycle. The count is slightly heavy but each tool maps to a real operation.

    Completeness4/5

    Coverage is broad: thread list/read/start/send/delegate/interrupt/open, worker send/read/list/status, project inspect/prepare, and bridge status/stop. Minor gaps exist (no thread archive/delete, no explicit worker task cancellation), but core lifecycles are covered.

    Maintenance

    ActivityActive
    ResponsivenessUsually handled

    Related MCP Connectors

    Related MCP Servers