Skip to main content
Glama
ar36planet

claude-codex-bridge

by ar36planet

claude-codex-bridge

CI

Claude Code が あなたの目の前にある Codex TUI に話しかける ―― その全過程を見られます。 逆に、Codex から Claude Code の実行中のセッションにメッセージを送ることもできます。

画面の文字認識、ファイルのポーリング、ヘッドレスサブエージェントではありません。両者は同じ codex app-server の同一スレッド上に接続されます。Claude Code が送ったメッセージは、 あなたが今見ている TUI 画面にリアルタイムで表示されます。

English: README.en.md

インストール方法はこちら:SETUP.md(中文)· SETUP.en.md(English)

この README は設計理由と検証記録について説明します ―― なぜこの方法を選んだのか、どの事実が すでに測定され、どの事実がまだ測定されていないのか。すぐに動かしたい場合は SETUP の方が早いです。

検証環境:codex-cli 0.147.0、Windows 11 と macOS 26 の両方で実測済み、 どちらも Node 24 LTS(Krypton)上で動作。コードはプラットフォームに依存しません(パスは常に node:path を使用、resolveCodex() の Windows ブランチのみ例外)。両環境の違いとそれぞれの 結果については、下記の「検証済み / 未検証」を参照してください。

必要な Node のバージョンは >=22(package.json の engines)、推奨は v24 LTS です。 プッシュのたびに CI で ubuntu / macOS / Windows × Node 22、24 の完全なマトリックスでテストされます。

codex は特定の nvm バージョン配下のグローバルパッケージであることが多く、そのバージョンが 22 未満である場合があります。 するとデフォルトの node も古くなります。最もクリーンな解決策は、node と codex を同じ LTS に揃えることです。

nvm install 24 && nvm alias default 24
nvm reinstall-packages 20        # 把 codex 等全域套件搬過去(20 換成你原本的版本)

(codex のバイナリは #!/usr/bin/env node のシムであり、インストール時のバージョンではなく PATH 上の node で実行されます。)

アーキテクチャ

        ┌──────────────────────────────┐
        │  codex app-server            │   ← 真正持有 thread 的地方
        │  --listen ws://127.0.0.1:8787│
        └───────┬──────────────┬───────┘
                │              │
   codex --remote ws://…       │  JSON-RPC over ws
                │              │
        ┌───────┴──────┐  ┌────┴─────────────┐
        │  Codex TUI   │  │  Claude Code     │
        │ (你在看)    │  │ (scripts/talk) │
        └──────┬───────┘  └────┬─────────────┘
               │               ▲
               └───────────────┘
        .bridge-inbox/<name>.jsonl → Stop hook
             (反方向:Codex → Claude Code)

順方向の鍵は thread/resume のセマンティクスです。

If thread_id identifies a running thread, app-server rejoins that thread.

つまり、2 番目のクライアントは新しい会話を開始するのでも、保存されたものを再生するのでもなく、同じ実行中のスレッドに参加します。 参加して初めて、そのスレッドの通知ストリームを受信できます。エンドポイントに接続するだけでは不十分です。

Related MCP server: Claude-Gemini MCP Integration Server

使い方

3 つのウィンドウ。

1. 共有サーバー(起動したまま閉じない)

node scripts/serve.mjs --cwd C:\path\to\你的專案

--cwd は Codex が実際に作業するディレクトリです。スレッドが独自に cwd を指定しない場合、app-server のものが使用されます。 そのため、このパラメータを指定しないと、スクリプトを起動した場所(つまり bridge 自身のフォルダ)で停止します。 環境変数 CODEX_BRIDGE_CWD でも指定できます。 ポートは --port または CODEX_BRIDGE_PORT(デフォルト 8787)で指定します。エンドポイントとワークスペースは .bridge.json に書き込まれ、talk.mjs が自動的に読み取ります。

2. あなたが見る Codex TUI

codex --remote ws://127.0.0.1:8787 -C C:\path\to\你的專案

-C でそのウィンドウのワークスペースを固定します。指定しない場合は、上記の --cwd で設定したものが使用されます。

まず TUI で一言話しかけ、それが返ってくるのを待ちます。 スレッドは最初のやり取りが「完了」して初めて再開可能(resumable)になります。 それ以前に thread/resume を実行すると no rollout found for thread id が返されます。

この落とし穴に注意:talk.mjs list はその前にそのスレッドをすでに表示できます ―― TUI が接続するとすぐにスレッドが作成され、 thread/loaded/list に表示されます。つまり、「list で見える」ことは「メッセージを送信できる」こととイコールではありません。 この手順をスキップすると、say は送信でき、Codex も TUI で応答しますが、bridge は応答ストリームを受信できず、 タイムアウトを待つだけになります(macOS でこの症状を確認済み)。

3. Claude Code 側

node scripts/talk.mjs list               # 列出活著的 thread(含各自的 cwd)
node scripts/talk.mjs say "跑一下測試"     # 送話進去,你會在 TUI 看到
node scripts/talk.mjs read               # 讀完整 thread(結構化 JSON)

スレッドが 1 つだけの場合は say / read が自動的に選択します。複数ある場合は --thread <id> で指定します ―― どのセッションと話しているのかを推測することはしません。list は各スレッドの cwd も一緒に表示するので、 複数起動時はこれで区別します。

say はさらに --cwd <dir>(このターンの作業ディレクトリのみ変更)と --approvals(下記参照)を受け付けます。

MCP インターフェース

CLI は引き続き直接使用可能です。MCP インターフェースは同じコア機能を構造化されたツールとして提供します。 両方向で同じコードを使用しますが、それぞれアクティブに発言する側が個別の STDIO プロセスを起動します。

  • --role claude:Claude Code が Codex スレッドに対してアクティブに発言します。

  • --role codex:Codex が Claude のメールボックスにメッセージを送信します。

インストール手順

0. 前提条件を確認

  • Node >=22(冒頭のバージョン説明を参照)。

  • このリポジトリで npm install を実行済みであること。

  • MCP はインターフェースであり、転送層ではありません。 動作している serve.mjs と接続された TUI がなければ、 会話する対象が存在しません ―― 上記「使い方」を参照。

cd <這個 repo>
npm install

1. どちら側をインストールするか決定

あなたが望むこと

インストールするもの

Claude Code が Codex に発言するのみ

--role claude のみ(Claude Code 側)

Codex が Claude Code にメッセージを残すのみ

--role codex のみ(Codex 側)

双方向

両方インストール

片方向のみ必要な場合は両方インストールしないでください。受動的に受信する側は MCP に依存しません ―― それぞれ app-server/TUI と Claude の Stop hook を使用します。

2. インストール

Claude Code 側(--role claude):

# macOS / Linux
claude mcp add --scope project claude-codex-bridge -- \
  node /path/to/claude-codex-bridge/scripts/mcp.mjs --role claude
# Windows
claude mcp add --scope project claude-codex-bridge -- `
  node "C:/path/to/claude-codex-bridge/scripts/mcp.mjs" --role claude

Codex 側(--role codex):

# macOS / Linux
codex mcp add claude-codex-bridge -- \
  node /path/to/claude-codex-bridge/scripts/mcp.mjs --role codex
# Windows
codex mcp add claude-codex-bridge -- `
  node "C:/path/to/claude-codex-bridge/scripts/mcp.mjs" --role codex

--scope project はそのプロジェクトの .mcp.json に書き込まれます。プロジェクト間で共有する場合は --scope user に変更します。

パスは絶対パスを使用しますが、「どのディレクトリで起動するか」は結果に影響しません ―― すべての状態ファイル (.bridge.json、.bridge-inbox/、.bridge-output/)はモジュールの位置から解決され、cwd は参照しません。 そのため、1 つのインストールで十分であり、プロジェクトごとにインストールする必要はありません。

3. 再起動

claude mcp add / codex mcp add は設定ファイルを変更するだけです。すでに実行中のセッションには新しい MCP server は読み込まれません。 インストール後はそのセッションを閉じて再起動すると、ツールが呼び出せるようになります。

4. インストールの確認

再起動したセッションで bridge_status を呼び出します。ok: true かつ role が正しければ成功です。 次に codex_threads_list で TUI のスレッド(cwd を含む)が表示されるはずです。

セッションを開かなくても、コマンドラインから同じパスを確認できます:

npm run test:e2e:mcp-send        # 需要 serve + 已跑完第一輪的 TUI

ツール

Role

Tools

共通

bridge_status、bridge_output_read

Claude

codex_threads_list、codex_thread_read、codex_message_send

Codex

claude_mailboxes_list、claude_mailbox_peek、claude_message_send

codex_message_send はターン全体を待機するため、Codex の MCP 設定ではタイムアウトを長めに設定し、write ツールに承認が必要なようにすることを推奨します:

[mcp_servers.claude-codex-bridge]
tool_timeout_sec = 360
default_tools_approval_mode = "writes"

MCP モードでは CODEX_BRIDGE_APPROVALS=tui(デフォルト)または decline のみ許可され、自動 accept は受け付けません。 短い応答はインラインで送信されます。64 KiB を超える場合は .bridge-output/ に書き込まれ、TTL 付きの不透明な artifact ID が返され、 bridge_output_read でページ分割して読み取ります。単一のキャプチャはデフォルトで最大 10 MiB であり、無制限の応答が 1 回のツール結果や Node ヒープに詰め込まれることはありません。

ローカル検証:

npm test                         # 語法、unit、in-memory MCP、真實 STDIO smoke;不呼叫模型
npm run test:integration:mcp-app-server  # 真實 app-server 連線,不建立模型 turn
npm run test:spikes              # 真實 app-server regression,可能使用模型
npm run test:e2e:mcp-send        # 完整 MCP → 真實 TUI;需要 serve + 已跑完第一輪的 TUI

test:e2e:mcp-send は他の spike とは異なります。新しい app-server を起動せず、.bridge.json に従って 現在の TUI に接続し、あなたが見ているスレッドに実際のターンを作成します。そのため、test:spikes には含まれておらず、 手動で実行する必要があります。

逆方向:Codex → Claude Code

Claude Code には同等の app-server がなく、何かをプッシュするためのソケットもありません。代わりに Stop hook があります。 Claude が終了しようとする直前にこのフックが実行され、{"decision":"block","reason":...} を返すことで、 停止を阻止し、reason を新しい入力として続行させることができます。

そこで、メールボックスを配置します。メールボックスは名前付きです ―― 複数の Claude Code セッションが同時に待機している可能性があり、 ファイルを共有すると、最初に終了したセッションが他の人のメッセージも消費してしまうからです:

# Codex 那側(或任何地方)留話
node scripts/inbox.mjs push --to bridge "順便幫我看一下 auth 那段"

# 現在有誰在聽(含各自的工作目錄)
node scripts/inbox.mjs list

# 看某個信箱(不消耗)
node scripts/inbox.mjs peek --as bridge

--to は「このメッセージの宛先」、--as は「読み取り元の自分」を指定し、どちらもデフォルトは $CODEX_BRIDGE_MAILBOX、 フォールバックは default です。

このリポジトリの .claude/settings.json にはすでに Stop hook(メールボックス名 bridge)が設定されています。 このプロジェクト内で Claude Code が終了する際、自動的にメールボックスを空にして続行します。そのファイルはバージョン管理されています。 そのため、このリポジトリをクローンして Claude Code で開くと、このフックが有効になります ―― メールボックスが空のときは完全に沈黙し、 不要であれば .claude/settings.json を削除するだけです。メッセージは1 回のみ配信されます。 drain() は最初にリネームしてから読み取るため、同時に書き込んでいる人によって読み取りの途中で中断されることはありません。

詳細とトレードオフについては docs/reverse-channel.md を参照してください。

他の Claude Code セッションでもこのブリッジを使用する

サーバーは 1 つ起動すれば十分で、他のセッションは共有します。スクリプトの状態(.bridge.json、メールボックス)は モジュール自身の位置から解決され、cwd は参照しないため、どのディレクトリから絶対パスで呼び出しても正しく動作します。

順方向(そのセッション → Codex):設定は不要で、直接呼び出します。

$bridge = "C:\path\to\claude-codex-bridge"
node "$bridge\scripts\talk.mjs" list
node "$bridge\scripts\talk.mjs" say --thread <threadId> "..."

複数の TUI を起動する場合は必ず --thread を指定してください ―― list は各スレッドの cwd を表示して識別できるようにします。 (または CODEX_BRIDGE_URL を設定すれば、.bridge.json に依存しません。)

逆方向(Codex → そのセッション):そのプロジェクトの .claude/settings.json に Stop hook を追加し、 独自のメールボックス名を指定します:

{ "hooks": { "Stop": [ { "matcher": "*", "hooks": [
  { "type": "command",
    "command": "node \"C:/path/to/claude-codex-bridge/scripts/inbox.mjs\" hook --as web" }
] } ] } }

ここで**$CLAUDE_PROJECT_DIR は使用できない**ことに注意 ―― それはそのプロジェクト自身を指し、bridge ではありません。 パスは bridge への絶対パスで固定する必要があります。メールボックス名(上の例では web)は自由に設定し、セッションごとに 1 つです。

その後、Codex 側から名前を指定してメッセージを送信できます:

node scripts/inbox.mjs push --to web "先把 CORS 那條修掉"
node scripts/inbox.mjs list       # 確認名字沒打錯、對方還活著

list のデータは、各セッションの Stop hook が実行されるたびに自己登録された情報に基づくため、そのセッションが少なくとも 1 回終了したことがある場合にのみリストに表示されます。

承認(Codex が何かを変更する場合)

Claude Code が送信したターンでコマンドを実行したりファイルを変更したりする場合、Codex は承認リクエストを発行します。 app-server はこのリクエストを接続されているすべてのクライアントにブロードキャストし、最初に応答したクライアントの決定が採用されます(間に合わなかったクライアントは serverRequest/resolved を受け取ります)。 そのため、デフォルトの戦略は tui:bridge は沈黙を保ち、あなたの目の前のウィンドウのプロンプトで決定させるです。

誰も応答しないとターン全体が停止するため、安全策があります。CODEX_BRIDGE_APPROVAL_TIMEOUT_MS(デフォルト 300 秒)を超えても応答がない場合、 bridge 自身がフェイルクローズ(拒否)し、ターンが進行できるようにします。

node scripts/talk.mjs say --approvals decline "..."   # 沒開 TUI 時用
node scripts/talk.mjs say --approvals accept  "..."   # 只用在你已經信任的環境

CODEX_BRIDGE_APPROVALS でデフォルト値を設定することもできます。

ブロードキャストのルーティングは macOS で実測確認済み(spike-approvals.mjs 11/11):1 つのクライアントが承認を押すと、 沈黙を保つ別のクライアントも同じリクエストを受け取り、その後 serverRequest/resolved を受け取り、ターンは正常に完了します。 したがって、「bridge の沈黙 = 人に決定させる」が成立します。

「TUI の人に任せる」を静かに無効にする 2 つの設定

承認リクエストがクライアントに到達する前に、まずユーザー自身の codex 設定を通過します。以下のいずれかが有効な場合、 あなたの目の前の TUI はそもそもプロンプトを表示されず、bridge の沈黙は人が決定していることになりません。

設定

位置

効果

PermissionRequest hook

~/.codex/hooks.json

hook が先に承認リクエストを横取りします。macOS 実測:hook が設定されている場合、2 つのクライアントは1 つも承認リクエストを受け取らず、ファイルはそのまま書き込まれます

approvals_reviewer = "auto_review"

~/.codex/config.toml

サブエージェントにリスクに応じて自動決定させ、人に問い合わせません

これらはどちらも妥当な個人設定であり、このプロジェクトはそれらを変更しません。ただし、注意すべき点は:それらが有効な場合、--approvals tui の「人」は実際にはそれらです。 自分のマシンがどの状態かを確認するには、spike-approvals.mjs を実行してください ―― その spike は独自の app-server を起動する際に --disable hooks -c approvals_reviewer=user でこれらを無効にし、プロトコル自体を測定します。

各種承認リクエストの応答形式は統一されていません ―― item/*/requestApproval の 2 つだけが {decision:"decline"} を受け付けます。 item/permissions/requestApproval は(空の)権限プロファイルを必要とし、古い execCommandApproval / applyPatchApproval は {decision:{denied:{rejection}}} を必要とします。間違った形式はスキーマエラーとなり、丁寧な拒否にはなりません。 対応表は src/appServerWsClient.mjs の DEFAULT_SERVER_REQUEST_RESPONSES にあります。

なぜ他の方法ではないのか

方法

問題

wezterm cli send-text / get-text

TUI のレンダリング後の画面を取得:枠線文字、スピナー、改行の切り詰め;「応答が終わったか」は画面の変化をポーリングするしかない

共有ファイルメールボックス(順方向)

可能だがリアルタイムの状態が見えず、トリガーに手動介入が必要

/codex:rescue subagent

毎回コールドスタート、独立したセッション、あなたの目の前の TUI には接続できない

本方式

構造化イベント;turn/steer で実行中のターンに割り込むことも可能

逆方向は依然としてファイルメールボックスを使用します ―― しかしそれは、Claude Code には接続可能なソケットがなく、Stop hook によって「トリガー」に手動介入が不要になるためです。

セキュリティ

  • Listener はループバックにバインドされます。--ws-auth は非ループバック接続に対してのみ有効なため、ローカルではトークンは不要です。

  • 承認はデフォルトで人に任せ(tui)、タイムアウト時はフェイルクローズします。承認以外のサーバー→クライアントリクエスト(ツール呼び出し、MCP elicitation)は常にフェイルクローズ ―― bridge には人に問い合わせる UI がありません。

検証済み / 未検証

3 つの spike。それぞれが独自の app-server(エフェメラルポート)を起動するため、現在見ているスレッドには影響しません:

node scripts/spike-multiclient.mjs   # 9/9   兩個 client 共用一條 thread
node scripts/spike-multithread.mjs   # 7/7   兩條 thread 同時跑,回覆不串味
node scripts/spike-approvals.mjs     # 11/11 於 macOS;Windows 上 3 項 SKIP,見下

macOS(26.5.1、Node v24.19.0 LTS、codex-cli 0.147.0)での実測結果:3 つの spike すべて合格、 npm test 21/21、npm run test:integration:mcp-app-server PASS。 scripts/serve.mjs --cwd、scripts/talk.mjs list、scripts/inbox.mjs (push / list / peek / hook、中国語を含む)も macOS で手動テスト済み。 inbox hook は Node 20 でも動作するため、Stop hook は Node のバージョンを選びません。

さらに、実際の TUI(codex --remote)を使用して実測確認済み:Claude Code が送信したメッセージは TUI 上で ユーザーメッセージとしてレンダリングされ、Codex は正常に応答し、応答ストリームは Claude Code に返されます。

確認済み(codex-cli 0.147.0):

  • 通知には常に threadId が含まれます。ストリーム系(item/agentMessage/delta)にはさらに turnId が含まれ、 turn/start が返す turn.id とストリーム上のものは完全に一致します。 初期には「通知に threadId が含まれない」と思われていましたが、実際には client がスレッドに join していない 症状でした。

  • スレッドはまず thread/resume で参加する必要があります。そうして初めて通知を受信できます。また、スレッドは最初のやり取りが完了した後にのみ再開可能(resumable)になります。

  • historyMode: "paginated"(TUI が作成したスレッド)→ thread/read に includeTurns を指定すると失敗します(list_turns is not supported yet)。事後的に履歴を取得するこの方法は現在利用できません。応答はリアルタイムストリームで受信します。

  • 承認はすべてのクライアントにブロードキャストされ、最初に応答したものが採用されます(macOS で確定)。沈黙しているクライアントもリクエストを受け取り、後で serverRequest/resolved を受け取るため、ターンは停止しません。 OS のサンドボックスヘルパーが起動できないマシン(特定の管理された企業 Windows では ShellExecuteExW failed to launch setup helper: 1223 が発生する場合)では、ファイル書き込みは「人に問い合わせる」前に失敗し、承認リクエスト自体が発行されないため、その 3 項目は SKIP とマークされます ―― 環境の制限であり、プロトコルの問題ではありません。

  • ユーザー層の PermissionRequest hook は承認リクエストを完全にブロックします。クライアントは 1 つも受信しません(macOS 実測)。詳細は上記「承認」の章を参照してください。

未検証:

  • turn/steer(実行中のターンに割り込む)はスキーマを読んだだけで、実測はしていません。

  • -C は実測していません → 実測済み:TUI が -C なしで接続した場合、スレッドの cwd は app-server の cwd になり、どのディレクトリで codex --remote を実行したかには関係ありません。-C <dir> を指定するとそのディレクトリに変更されます。(macOS、pty で TUI を起動し、別のクライアントで thread/read を読み取り。)

  • プロトコル全体が [experimental] とマークされているため、codex のバージョンアップで変更される可能性があります。

参考

逆方向に Stop hook を選んだ理由、他のメカニズムではない理由: docs/reverse-channel.md

プロトコルスキーマ:codex app-server generate-json-schema --out <dir>

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    The self-hosted MCP bridge between Claude Chat and Claude Code.
    46
    AGPL 3.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    Connects Claude Desktop and Claude Code, enabling autonomous exchange of messages, files, and code while keeping their context windows separate.
    4
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Bridges Claude Code and OpenAI Codex CLI for an interactive plan-execute-review workflow, enabling Claude to interview, design, and review while Codex implements code changes.
    7
    29 npm
    3
    MIT