codex-mcp-bridge

codex-mcp-bridge

MCP server for Claude Desktop to send prompts into a live Codex thread through a shared Codex app-server, preserving thread history, working directory, and model.

Category
访问服务器

README

codex-mcp-bridge

English version

Cầu hai chiều giữa Claude và Codex: Claude gửi prompt vào thread Codex đang mở, Codex nhắn ngược vào phiên Claude Code đang chạy. Mỗi bên đều thấy phiên của bên kia và nhìn được hội thoại ngay trong app của mình. Chạy trên macOS, Windows và Linux (chiều Codex → Claude cần unix socket nên chỉ macOS/Linux).

Không phải codex exec (tạo phiên mới mỗi lần). Bridge nói JSON-RPC với app-server thật của Codex, nên thread giữ nguyên lịch sử, cwd, model và rollout file.

Kiến trúc

Hai MCP server, mỗi cái sống trong một agent:

                     ┌──────────────── codex-mcp-bridge (chạy trong Claude) ────────────────┐
Claude Desktop ──────┤ stdio                                    WebSocket                   ├──> codex app-server ──> thread hiện trong Codex Desktop
                     └─────────────────────────────────────────────────────────────────────┘

                     ┌──────────────── claude-bridge (chạy trong Codex) ───────────────────┐
Codex ───────────────┤ stdio                       unix socket /tmp/cc-socks/<pid>.sock    ├──> phiên Claude Code ──> tin nhắn hiện trong Claude Desktop
                     └─────────────────────────────────────────────────────────────────────┘

Codex TUI  ──codex --remote ws://127.0.0.1:8791──> cùng app-server, cùng thread live
  • App-server là singleton theo port. Bridge probe http://127.0.0.1:8791/readyz; nếu chưa sống thì tự spawn detached (codex app-server --listen ws://127.0.0.1:8791) và app-server đó tiếp tục chạy độc lập sau khi bridge thoát.
  • Mọi client trỏ cùng URL đều dùng chung một app-serverthread/resume bằng threadId sẽ rejoin đúng thread đang chạy thay vì mở phiên mới.
  • Bridge giữ đúng một WebSocket, initialize một lần, và route notification theo threadId nên nhiều thread chạy song song không lẫn nhau.

Tools — codex-mcp-bridge (cài vào Claude)

Tool Việc
send_to_codex_thread Gửi prompt như một user turn vào threadId, chờ turn/completed, trả lời của Codex + trail hoạt động (lệnh đã chạy, file đã sửa).
list_codex_threads Liệt kê thread (id, title, cwd, thời điểm cập nhật, status) — dùng để lấy đúng threadId. loadedOnly: true chỉ hiện thread đang live trong app-server. Trên macOS mỗi dòng kèm luôn deep link codex://threads/<id>.
start_codex_thread Mở thread Codex mới tại một cwd, trả threadId.
read_codex_thread Đọc hội thoại gần đây của thread, không gửi gì.
interrupt_codex_turn Dừng một turn đang chạy.
open_codex_thread macOS: bật thread lên trong Codex desktop app qua codex://threads/<id> để người dùng xem trực tiếp. background: true để mở mà không cướp focus.
stop_codex_app_server Dừng app-server dùng chung sau khi giao việc xong — tránh để nó tranh chấp state ~/.codex với Codex Desktop. Bridge tự bật lại khi cần lần sau.
codex_bridge_status Báo cáo môi trường: platform, codex binary đã resolve, endpoint app-server còn sống không, LaunchAgent + desktop app trên macOS, và cảnh báo khi hai app-server cùng chạy. Dùng đầu tiên khi bridge có vấn đề.

send_to_codex_thread nhận thêm timeoutSec (mặc định 240), cwd, model, effort, và openInApp (macOS — mở thread trong app trước khi gửi để xem live). Hết thời gian chờ không hủy turn — bridge trả về những gì đã thu được kèm turnId; đọc tiếp bằng read_codex_thread hoặc dừng bằng interrupt_codex_turn.

Tools — claude-bridge (cài vào Codex)

Tool Việc
list_claude_sessions Liệt kê phiên Claude Code đang chạy trên máy (tên, pid, sessionId, cwd, khởi động từ đâu).
send_to_claude_session Gửi tin nhắn vào phiên Claude — hiện thẳng trong khung chat của phiên đó y như tin từ đồng đội — rồi chờ Claude trả lời. waitSec: 0 để gửi xong đi luôn. target nhận tên, pid hoặc sessionId; tên phiên Claude tự đổi theo thời gian nên nhắm bằng sessionId mới chắc.
read_claude_inbox Đọc (và xoá) những tin Claude chủ động đẩy sang, kể cả trả lời tới muộn.
read_claude_transcript Đọc hội thoại gần đây của một phiên Claude mà không gửi gì.
bind_codex_thread Gắn một thread Codex để mọi tin từ Claude được relay vào thread đó → hiện trong Codex Desktop. Truyền chuỗi rỗng để tắt.
claude_bridge_status Báo cáo peer endpoint, số phiên Claude đang sống, thread đang relay, số tin trong inbox.

Hai bên nhìn thấy nhau thế nào

  • Claude thấy Codex: claude-bridge tự đăng ký thành một peer session trong ~/.claude/sessions/. Claude liệt kê nó bằng ListAgents và nhắn sang bằng SendMessage — không ẩn, không phải phiên nền vô hình. Tên mặc định là codex-<pid>; sau bind_codex_thread nó tự đổi thành codex-<8 ký tự đầu threadId> để phân biệt được từng thread (Codex spawn một bridge cho mỗi phiên, nên thường có vài peer cùng lúc).
  • Codex thấy Claude: list_claude_sessions đọc đúng registry đó, kèm read_claude_transcript để xem phiên Claude đang làm gì.
  • Hiện trong chat: tin Codex gửi vào phiên Claude xuất hiện trong khung chat Claude Desktop; tin Claude gửi về được relay vào thread Codex (sau bind_codex_thread) nên hiện trong Codex Desktop.

Protocol (đo thực tế, không có tài liệu chính thức)

Mỗi phiên Claude Code ghi ~/.claude/sessions/<pid>.json và nghe trên /tmp/cc-socks/<pid>.sock. Khung là NDJSON, một dòng một tin:

{"msgV":1,"msg_id":"<uuid>","type":"user","message":{"role":"user",
 "content":"<cross-session-message from=\"uds:/tmp/cc-socks/<pid>.sock\" from-mode=\"bypass\">\n...\n</cross-session-message>"},
 "priority":"next","from":"uds:/tmp/cc-socks/<pid>.sock"}

Không có token trong khung — socket để mode 0600 nên chỉ user sở hữu mới gửi được, đó là toàn bộ ranh giới bảo mật. Muốn nhận trả lời thì phải tự đăng ký một peer session (registry + socket), vì Claude trả lời về địa chỉ trong from.

⚠️ Đây là cơ chế nội bộ của Claude Code, không có tài liệu công khai (đo trên bản 2.1.229). Claude Code đổi format là chiều Codex → Claude gãy — sửa ở src/peer-protocol.mjs. Chiều Claude → Codex đi qua app-server chính thức nên không dính.

Chống ping-pong vô hạn

Relay có hai chốt chặn cứng trong src/claude-bridge.mjs: tối đa 1 tin mỗi 5s50 tin mỗi lần chạy bridge. Hai agent tự nói chuyện với nhau mà không ai trông thì vẫn dừng lại được.

Cài vào Claude Desktop

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

Script tự nhận platform, tạo file config nếu chưa có, backup bản cũ (*.bak-<ngày>-codexbridge) và giữ nguyên mọi key sẵn có:

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

Kết quả trên 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 sau khi cài.

Resolve codex binary: Claude Desktop (và launchd) khởi chạy MCP server với PATH bị cắt gọn nên codex thường không có trên PATH. Bridge dò theo thứ tự — CODEX_BIN → các vị trí cài quen thuộc của platform → PATH:

OS Thứ tự dò
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 (chỉ macOS)
Windows %LOCALAPPDATA%\Programs\OpenAI\Codex\bin\codex.exe%APPDATA%\npm\codex.cmd%ProgramFiles%\nodejs\codex.cmd

Trên macOS/Linux, codex là script Node có shebang #!/usr/bin/env node, nên bridge còn bơm lại PATH (thư mục node hiện tại + /opt/homebrew/bin + /usr/local/bin + system dirs) cho tiến trình con — thiếu bước này thì spawn app-server chết ngay từ shebang.

Cài vào Codex (chiều ngược)

node scripts/install-codex-mcp.mjs

Chạy codex mcp add claude-bridge -- <node> src/claude-bridge.mjs, ghi vào ~/.codex/config.toml. Kiểm tra bằng codex mcp list, gỡ bằng node scripts/install-codex-mcp.mjs --remove.

Khởi động lại Codex app (hoặc mở phiên Codex mới) để nạp. Nếu app-server dùng chung đang chạy sẵn (LaunchAgent), nạp config mới bằng:

launchctl kickstart -k gui/$UID/com.codex-mcp-bridge.app-server

Đặt tên peer khác codex bằng CLAUDE_BRIDGE_PEER_NAME — đây là tên Claude nhìn thấy trong danh sách agent.

macOS

App-server chạy nền bằng launchd

⚠️ Đừng bật LaunchAgent nếu sếp dùng Codex Desktop. App có app-server riêng (stdio) chạy trên cùng state sqlite ~/.codex. Hai app-server cùng nằm im vẫn tranh chấp state — đã đo: bản launchd ăn ~11% CPU lúc rảnh và giao diện Codex app bị giật. Chỉ giữ một cái sống. codex_bridge_status phát hiện và cảnh báo tình huống này.

LaunchAgent hợp lý khi chạy không có Codex Desktop (server headless, máy chỉ dùng CLI/TUI). Còn khi dùng app: bỏ LaunchAgent, để bridge tự spawn app-server lúc cần — tranh chấp chỉ kéo dài trong lúc giao việc thay vì 24/7.

node scripts/install-launch-agent.mjs

Tạo ~/Library/LaunchAgents/com.codex-mcp-bridge.app-server.plist (RunAtLoad + KeepAlive khi crash, ThrottleInterval 10s) rồi launchctl bootstrap gui/$UID. App-server sống sẵn từ lúc đăng nhập nên bridge không phải tự spawn, và thread luôn ở trạng thái live.

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.

Xem thread trực tiếp trong Codex desktop app

Codex desktop app trên macOS là /Applications/ChatGPT.app và đăng ký scheme codex://. Bridge dùng codex://threads/<threadId> để mở đúng thread:

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

Đây là cách để người giao việc nhìn thấy Codex đang làm thay vì phải đọc lại rollout ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl sau khi xong.

Giới hạn trên macOS

  • Codex desktop app tự chạy app-server riêng qua stdio (ChatGPT.app/Contents/Resources/codex … app-server, không--listen) nên không nối vào được từ ngoài. ~/.codex/ipc/ipc.sock là IPC nội bộ của Electron app, không phải app-server. Thread mở trong app vẫn gửi được qua bridge, nhưng theo cơ chế resume từ rollout .jsonl chứ không phải attach live.
  • Thread đang mở trong desktop app thì không gửi vào được — Codex khoá writer theo thread (~/.codex/thread-writer-locks/) và trả lỗi thread <id> already has an active writer. Lỗi này là bảo vệ, không phải hỏng dữ liệu. Kiểm tra status bằng list_codex_threads trước, chỉ gửi khi idle/notLoaded và thread không đang mở trong app.
  • Thread do bridge tạo không tự hiện tên trong app. App liệt kê theo ~/.codex/session_index.jsonl, mà mục ở đó chỉ được ghi khi thread đã được đặt tên — việc đặt tên do app làm, không phải app-server. Thread vẫn nằm trong ~/.codex/state_5.sqlite và mở được bằng deep link codex://threads/<id>.
  • Repo đặt trên phân vùng NTFS của máy dual-boot (/Volumes/...) chỉ đọc được trên macOS — macOS mount NTFS read-only. Giữ một checkout riêng trên ổ APFS (vd ~/code/codex-mcp-bridge) để chạy và sửa.
  • codex app-server daemon start dùng transport unix:// với control socket ~/.codex/app-server-control/app-server-control.sock. Bridge không dùng đường này (giao thức khung khác WebSocket, chưa có API công khai) — luôn nói chuyện qua ws://.

Sự cố thường gặp

readyz không bao giờ trả 200 sau khi restart app-server. Log có failed to initialize sqlite state runtime under ~/.codex. Nguyên nhân: còn một app-server cũ chưa chết hẳn đang giữ state sqlite của ~/.codex — chỉ một tiến trình được giữ nó. Kill cứng (pkill -9) hay launchctl kickstart -k liên tiếp dễ để lại zombie mà pkill -f "app-server --listen ws://…" không khớp vì tên tiến trình là đường dẫn binary vendor.

ps aux | grep "[a]pp-server --listen"
pkill -9 -f "codex-darwin-arm64/vendor.*app-server"
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.codex-mcp-bridge.app-server.plist

Codex treo khi mở thread mới sau khi thêm MCP server. Client MCP chờ handshake initialize; server chết trước đó thì biểu hiện là treo, không phải lỗi. Đã trả giá thật: claude-bridge gọi execFileSync("ps", …) mà Codex spawn MCP server với PATH rỗngENOENT → chết trước handshake → mọi thread/start timeout 60s. Fix: gọi /bin/ps bằng đường dẫn tuyệt đối và bọc try/catch (src/peer-protocol.mjs). Bài học chung: MCP server không được phụ thuộc PATH của tiến trình cha — luôn test bằng env -i PATH="" node <server> trước khi ship.

Env

Biến Mặc định Ý nghĩa
CODEX_APP_SERVER_URL ws://127.0.0.1:8791 Endpoint app-server dùng chung.
CODEX_BIN tự dò Đường dẫn codex để autostart.
CODEX_BRIDGE_AUTOSTART 1 0 = không tự spawn app-server, bắt buộc phải có sẵn.
CODEX_BRIDGE_APPROVAL approve Cách trả lời approval request từ Codex. Đặt deny để từ chối.
CODEX_BRIDGE_MODEL theo ~/.codex/config.toml Model mặc định cho thread/turn bridge tạo, vd gpt-5.6-luna.
CODEX_BRIDGE_EFFORT theo ~/.codex/config.toml Reasoning effort mặc định: minimal · low · medium · high · xhigh · ultra.
CLAUDE_DESKTOP_CONFIG tự dò theo OS Ép đường dẫn config khi chạy install-claude-desktop.mjs.
CODEX_EXE tự dò Ép đường dẫn codex cho hai script cài đặt.

Về model và effort: Codex Desktop không đọc model/model_reasoning_effort trong ~/.codex/config.toml — nó tự chạy cấu hình riêng. Nên thread mở qua bridge dễ yếu hơn cùng việc làm trong app mà không ai nhận ra. Đặt CODEX_BRIDGE_MODEL + CODEX_BRIDGE_EFFORT (trong env của MCP server) để hai đường ra cùng chất lượng; codex_bridge_status in ra giá trị đang dùng. Kiểm chứng bằng cách đọc thẳng state của Codex thay vì tin là lệnh đã có tác dụng:

sqlite3 ~/.codex/state_5.sqlite "select model, reasoning_effort from threads order by updated_at desc limit 3;"

Về approval: Codex sẽ hỏi duyệt lệnh/patch nếu approval_policy không phải never. Không ai ngồi trước Claude Desktop để bấm, nên bridge tự trả lời theo CODEX_BRIDGE_APPROVAL và log ra stderr. Mặc định approve khớp với cấu hình approval_policy = "never" + sandbox_mode = "danger-full-access" trong ~/.codex/config.toml; nếu siết sandbox lại thì cân nhắc đổi sang deny.

Dùng chung app-server với phiên Codex tương tác

Mở TUI trỏ vào cùng endpoint để thread trong TUI và thread bridge nhìn thấy là một:

codex --remote ws://127.0.0.1:8791

Chạy app-server thủ công (không phụ thuộc bridge autostart):

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

Test

npm run check

Kiểm tra nhanh: bridge khởi động, autostart app-server nếu cần, liệt kê thread.

npm run smoke

Smoke test tạo thread mới, gửi 2 turn liên tiếp và kiểm tra Codex nhớ được codeword từ turn trước — tức thread thật sự liên tục chứ không phải phiên mới mỗi lần.

npm run check:claude

Kiểm tra chiều Codex → Claude: liệt kê phiên Claude đang sống. Muốn thử gửi thật thì đặt biến môi trường:

CLAUDE_TARGET=<sessionId> CLAUDE_WAIT=150 npm run check:claude

Script gửi một tin vào phiên đó rồi chờ Claude trả lời — trả lời về được nghĩa là cả hai chiều đều thông.

Kiểm tra môi trường từ trong Claude: gọi tool codex_bridge_status. Từ trong Codex: claude_bridge_status.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选