claude-codex-bridge
claude-codex-bridge
Permite que Claude Code le hable al Codex TUI que tienes delante — y tú lo ves todo en tiempo real. A la inversa, Codex también puede enviar mensajes a la sesión que está ejecutando Claude Code.
No es captura de pantalla, ni sondeo de archivos, ni un subagente sin interfaz. Ambos están conectados al
mismo hilo del codex app-server: el mensaje que Claude Code envía aparece al instante
en la interfaz TUI que estás viendo.
English: README.en.md
Para instalarlo, mira aquí: SETUP.md (中文) · SETUP.en.md (English)
Este README trata sobre la justificación del diseño y el registro de validación — por qué se hizo así, qué hechos ya se han medido y cuáles no. Si quieres ponerlo en marcha directamente, SETUP es más rápido.
Entorno de validación: codex-cli 0.147.0, probado tanto en Windows 11 como en macOS 26,
ambos ejecutándose en Node 24 LTS (Krypton). El código no está vinculado a la plataforma (las rutas siempre usan
node:path, solo la rama de Windows en resolveCodex() es una excepción). Las diferencias entre ambos y los resultados
obtenidos se encuentran en la sección "Validado / No validado" más abajo.
El requisito de Node es >=22 (el campo engines de package.json), se recomienda usar directamente v24 LTS.
Cada push ejecuta una matriz completa de ubuntu / macOS / Windows × Node 22, 24 en CI.
codex suele ser un paquete global bajo alguna versión de nvm, y esa versión puede ser inferior a 22 — por lo tanto, el node
por defecto también se vuelve antiguo. La solución más limpia es mantener node y codex en la misma LTS:
nvm install 24 && nvm alias default 24
nvm reinstall-packages 20 # 把 codex 等全域套件搬過去(20 換成你原本的版本)(El binario de codex es un shim con #!/usr/bin/env node, que se ejecuta con el node del PATH,
no con la versión con la que se instaló.)
Arquitectura
┌──────────────────────────────┐
│ 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)La clave en la dirección directa es la semántica de thread/resume:
If thread_id identifies a running thread, app-server rejoins that thread.
Por lo tanto, el segundo cliente no inicia una nueva conversación ni reproduce un archivo guardado — se une al mismo hilo que ya está ejecutándose. Solo después de unirse se puede recibir el flujo de notificaciones de ese hilo; no basta con conectarse al endpoint.
Related MCP server: Claude-Gemini MCP Integration Server
Uso
Tres ventanas.
1. Servidor compartido (mantenlo abierto, no lo cierres)
node scripts/serve.mjs --cwd C:\path\to\你的專案--cwd es el directorio donde Codex trabaja realmente. Cuando el hilo no especifica su propio cwd, hereda el del
app-server, así que si no se proporciona este parámetro, se detendrá en el directorio donde iniciaste el script (es decir, la
carpeta del propio bridge). También se puede usar la variable de entorno CODEX_BRIDGE_CWD.
El puerto se define con --port o CODEX_BRIDGE_PORT (por defecto 8787). El endpoint y el workspace
se escriben en .bridge.json, y talk.mjs lo lee automáticamente.
2. El Codex TUI que quieres ver
codex --remote ws://127.0.0.1:8787 -C C:\path\to\你的專案-C fija el workspace de esa ventana; si no se proporciona, se usará el --cwd establecido arriba.
Primero di algo en el TUI y espera a que responda. El hilo debe haber completado su primera ronda de conversación para poder
reanudarse; antes de eso, thread/resume devolverá no rollout found for thread id.
Ten en cuenta esta trampa: talk.mjs list ya ve ese hilo antes de eso — el TUI crea el hilo en cuanto se conecta y
aparece en thread/loaded/list. Por lo tanto, "list lo ve" no significa que "se pueda enviar un mensaje". Si te saltas
este paso, say enviará el mensaje, Codex responderá en el TUI, pero el bridge no recibirá
el flujo de respuesta, solo esperará hasta que se agote el tiempo de espera (síntoma probado en macOS).
3. El lado de Claude Code
node scripts/talk.mjs list # 列出活著的 thread(含各自的 cwd)
node scripts/talk.mjs say "跑一下測試" # 送話進去,你會在 TUI 看到
node scripts/talk.mjs read # 讀完整 thread(結構化 JSON)Cuando solo hay un hilo, say / read lo seleccionan automáticamente; cuando hay varios, hay que especificarlo con --thread <id> —
no se adivina con qué sesión estás hablando. list imprime el cwd de cada hilo, lo que ayuda a distinguirlos
cuando hay varias ventanas abiertas.
say también acepta --cwd <dir> (cambia el directorio de trabajo solo para esta ronda) y --approvals (ver más abajo).
Interfaz MCP
La CLI sigue siendo utilizable directamente; la interfaz MCP proporciona herramientas estructuradas con las mismas capacidades principales. Ambas direcciones comparten el mismo código, pero el extremo que inicia la conversación ejecuta su propio proceso STDIO:
--role claude: Claude Code envía mensajes activamente al hilo de Codex.--role codex: Codex coloca mensajes activamente en el buzón de Claude.
Pasos de instalación
0. Verifica los requisitos previos
Node
>=22(consulta la nota sobre versiones al principio).Este repositorio ya ha ejecutado
npm install.MCP es solo una interfaz, no una capa de transporte. Igualmente necesita un
serve.mjsen ejecución y un TUI conectado para tener algo con lo que hablar — consulta la sección "Uso" más arriba.
cd <這個 repo>
npm install1. Decide qué lado instalar
Lo que quieres | Instala esto |
Solo que Claude Code pueda hablar a Codex | Solo instala |
Solo que Codex pueda dejar mensajes a Claude Code | Solo instala |
Bidireccional | Instala ambos |
Si solo necesitas una dirección, no instales ambos lados. La mitad receptora pasiva no depende de MCP — utiliza el app-server/TUI y el Stop hook de Claude respectivamente.
2. Instalación
Lado de 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 claudeLado de 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 escribe en el .mcp.json de ese proyecto; para usarlo en varios proyectos, cámbialo a --scope user.
Usa rutas absolutas, pero "en qué directorio se inicia" no afecta el resultado — todos los archivos de estado
(.bridge.json, .bridge-inbox/, .bridge-output/) se resuelven desde la ubicación del módulo,
no desde el cwd. Por lo tanto, una sola instalación es suficiente, no es necesario instalar para cada proyecto.
3. Reinicia
claude mcp add / codex mcp add solo modifican el archivo de configuración, la sesión que ya está ejecutándose no cargará el nuevo
servidor MCP. Después de la instalación, cierra y vuelve a abrir esa sesión para que las herramientas estén disponibles.
4. Verifica la instalación
En la sesión reiniciada, llama a bridge_status. Si ok: true y el role son correctos, está bien.
Luego, codex_threads_list debería mostrar el hilo de tu TUI (incluyendo su cwd).
Si no quieres abrir una sesión, también puedes verificar la misma ruta desde la línea de comandos:
npm run test:e2e:mcp-send # 需要 serve + 已跑完第一輪的 TUIHerramientas
Rol | Herramientas |
Compartidas |
|
Claude |
|
Codex |
|
codex_message_send espera todo el turno, por lo que se recomienda aumentar el tiempo de espera en la configuración MCP de Codex y requerir
aprobación para las herramientas de escritura:
[mcp_servers.claude-codex-bridge]
tool_timeout_sec = 360
default_tools_approval_mode = "writes"El modo MCP solo permite CODEX_BRIDGE_APPROVALS=tui (por defecto) o decline, no acepta accept
automático. Las respuestas cortas se envían directamente en línea; las que superan los 64 KiB se escriben en .bridge-output/,
devolviendo un ID de artifact opaco con TTL, que luego se lee por páginas con bridge_output_read. Una sola captura tiene un máximo
predeterminado de 10 MiB, para evitar que una respuesta ilimitada sature el resultado de la herramienta o el heap de Node.
Validación local:
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 + 已跑完第一輪的 TUItest:e2e:mcp-send es diferente de otros spikes: no inicia un app-server separado, sino que se conecta
al TUI que tienes abierto según .bridge.json, creando un turno real en el hilo que estás viendo. Por lo tanto,
no está en test:spikes, hay que ejecutarlo manualmente.
Dirección inversa: Codex → Claude Code
Claude Code no tiene un app-server equivalente, no hay un socket al que empujar datos. Lo que tiene es un
Stop hook: Claude lo ejecuta antes de finalizar, y si el hook devuelve {"decision":"block","reason":...},
puede evitar que se detenga y usar reason como nueva entrada para continuar.
Por lo tanto, se coloca un buzón en el medio. El buzón tiene nombre — porque puede haber varias sesiones de Claude Code escuchando al mismo tiempo, y si comparten un archivo, la primera que termine se tragaría los mensajes de las demás:
# Codex 那側(或任何地方)留話
node scripts/inbox.mjs push --to bridge "順便幫我看一下 auth 那段"
# 現在有誰在聽(含各自的工作目錄)
node scripts/inbox.mjs list
# 看某個信箱(不消耗)
node scripts/inbox.mjs peek --as bridge--to es "a quién va dirigido este mensaje", --as es "quién soy yo al leerlo". Ambos por defecto toman $CODEX_BRIDGE_MAILBOX
y luego default.
El .claude/settings.json de este repositorio ya tiene el Stop hook configurado (nombre del buzón bridge).
Cuando Claude Code finalice su trabajo en este proyecto, vaciará automáticamente el buzón y continuará. Ese archivo está
versionado, por lo que si clonas el repositorio y lo abres con Claude Code, este hook entrará en vigor automáticamente — cuando el buzón
esté vacío, permanecerá completamente en silencio; si no lo necesitas, elimina .claude/settings.json. Los mensajes
se entregan solo una vez: drain() primero renombra y luego lee, por lo que alguien que esté escribiendo al mismo tiempo no leerá
datos a medias.
Para más detalles y compensaciones, consulta docs/reverse-channel.md.
Hacer que otras sesiones de Claude Code también usen este puente
El servidor solo necesita iniciarse una vez, las demás sesiones lo comparten. El estado del script (.bridge.json, buzones) se
resuelve desde la ubicación del propio módulo, no desde el cwd, por lo que llamarlo con una ruta absoluta desde cualquier directorio funciona correctamente.
Dirección directa (esa sesión → Codex): No requiere configuración, se llama directamente.
$bridge = "C:\path\to\claude-codex-bridge"
node "$bridge\scripts\talk.mjs" list
node "$bridge\scripts\talk.mjs" say --thread <threadId> "..."Cuando hay múltiples TUI abiertos, asegúrate de usar --thread — list mostrará el cwd de cada hilo para que los identifiques.
(O configura CODEX_BRIDGE_URL, para no depender de .bridge.json).
Dirección inversa (Codex → esa sesión): Debes agregar el Stop hook en el .claude/settings.json de ese proyecto
y darle un nombre de buzón propio:
{ "hooks": { "Stop": [ { "matcher": "*", "hooks": [
{ "type": "command",
"command": "node \"C:/path/to/claude-codex-bridge/scripts/inbox.mjs\" hook --as web" }
] } ] } }Ten en cuenta que no se puede usar $CLAUDE_PROJECT_DIR — eso apuntaría al propio proyecto, no al bridge.
La ruta debe estar fija en el bridge. El nombre del buzón (web en el ejemplo) lo eliges tú, uno por sesión.
Luego, desde el lado de Codex, puedes enviar mensajes con nombre:
node scripts/inbox.mjs push --to web "先把 CORS 那條修掉"
node scripts/inbox.mjs list # 確認名字沒打錯、對方還活著Los datos de list provienen del auto-registro de cada sesión cada vez que se ejecuta su Stop hook, por lo que esa
sesión debe haberse finalizado al menos una vez para aparecer en la lista.
Aprobación (cuando Codex necesita modificar algo)
Si el turno enviado por Claude Code va a ejecutar comandos o modificar archivos, Codex emitirá una solicitud de aprobación.
El app-server difunde esta solicitud a todos los clientes conectados, y el primero que responda es el que cuenta (los que no
lo consiguen reciben serverRequest/resolved). Por lo tanto, la estrategia predeterminada es tui: el bridge permanece en silencio, dejando que la decisión
la tome el indicador de la ventana que tienes delante.
Si nadie responde, todo el turno se queda bloqueado, por lo que hay un seguro: si nadie responde después de CODEX_BRIDGE_APPROVAL_TIMEOUT_MS
(predeterminado 300 segundos), el propio bridge rechaza con fail-closed para que el turno pueda continuar.
node scripts/talk.mjs say --approvals decline "..." # 沒開 TUI 時用
node scripts/talk.mjs say --approvals accept "..." # 只用在你已經信任的環境También se puede usar CODEX_BRIDGE_APPROVALS para establecer el valor predeterminado.
La ruta de difusión ya está confirmada en macOS (spike-approvals.mjs 11/11): un cliente
presiona aprobar, otro cliente que permanece en silencio recibe la misma solicitud y luego recibe
serverRequest/resolved, y el turno continúa con normalidad. Por lo tanto, "bridge silencioso = dejar que alguien decida" es válido.
Dos configuraciones que pueden hacer que "dejar que la persona del TUI decida" falle silenciosamente
Antes de que la solicitud de aprobación llegue a cualquier cliente, primero pasa por la configuración de codex del usuario. Si alguna de las siguientes está activa, el TUI que tienes delante ni siquiera será preguntado, y el silencio del bridge no significa que la persona esté decidiendo:
Configuración | Ubicación | Efecto |
Hook |
| El hook intercepta la solicitud de aprobación primero. Probado en macOS: con el hook activo, los dos clientes no reciben ninguna solicitud de aprobación, pero el archivo se escribe igual |
|
| Se lo pasa a un subagente que decide automáticamente según el riesgo, sin preguntar a la persona |
Ambas son configuraciones personales razonables; este proyecto no las modificará. Solo hay que saber: cuando están activas,
la "persona" en --approvals tui son en realidad ellas. Para confirmar en qué estado está tu máquina, ejecuta
spike-approvals.mjs — ese spike inicia su propio app-server con
--disable hooks -c approvals_reviewer=user para desactivar ambas, midiendo solo el protocolo en sí.
El formato de respuesta para las diversas solicitudes de aprobación no es consistente — solo dos item/*/requestApproval aceptan
{decision:"decline"}; item/permissions/requestApproval requiere un perfil de permisos (vacío),
y los antiguos execCommandApproval / applyPatchApproval requieren
{decision:{denied:{rejection}}}. Responder con la forma incorrecta es un error de esquema, no un rechazo cortés.
La tabla de correspondencia está en src/appServerWsClient.mjs en DEFAULT_SERVER_REQUEST_RESPONSES.
Por qué no otras soluciones
Solución | Problema |
| Captura la pantalla renderizada del TUI: caracteres de borde, spinner, saltos de línea truncados; determinar "si ha terminado" solo se puede hacer sondeando los cambios en la pantalla |
Buzón de archivos compartidos (dirección directa) | Factible pero no se ve el estado en tiempo real, y la activación requiere intervención manual |
Subagente | Cada inicio en frío, sesión independiente, no se conecta al TUI que tienes delante |
Esta solución | Eventos estructurados; |
La dirección inversa sigue siendo un buzón de archivos — pero eso es porque Claude Code no tiene un socket al que conectarse, mientras que el Stop hook hace que la "activación" no requiera intervención manual.
Seguridad
El listener se vincula a loopback.
--ws-authsolo tiene efecto en conexiones non-loopback, por lo que en local no se necesita token.La aprobación se delega por defecto a la persona (
tui), con fail-closed en caso de tiempo de espera agotado. Las solicitudes de servidor a cliente que no son de aprobación (llamadas a herramientas, MCP elicitation) siempre fallan con fail-closed — el bridge no tiene una interfaz de usuario para preguntar a la persona.
Validado / No validado
Tres spikes, cada uno inicia su propio app-server (puerto efímero), sin afectar el hilo que estás viendo:
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,見下Resultados de la ejecución real en macOS (26.5.1, Node v24.19.0 LTS, codex-cli 0.147.0): los tres spikes pasaron,
npm test 21/21, npm run test:integration:mcp-app-server PASS.
scripts/serve.mjs --cwd, scripts/talk.mjs list, y scripts/inbox.mjs
(push / list / peek / hook, incluido chino) también se probaron manualmente en macOS;
el inbox hook funciona incluso con Node 20, por lo que el Stop hook no necesita una versión específica de Node.
Además, se confirmó con un TUI real (codex --remote): el mensaje enviado por Claude Code se renderiza
como un mensaje de usuario en el TUI, Codex responde con normalidad, y el flujo de respuesta vuelve a Claude Code.
Ya aclarado (codex-cli 0.147.0):
Todas las notificaciones llevan
threadId, las de tipo flujo (item/agentMessage/delta) también llevanturnId, y elturn.iddevuelto porturn/startcoincide exactamente con el del flujo. Inicialmente se pensó que "las notificaciones no llevan threadId", pero en realidad era un síntoma de que el cliente no se había unido al hilo.El hilo debe unirse primero con
thread/resumepara recibir notificaciones; y el hilo debe haber completado la primera ronda de conversación para poder reanudarse.historyMode: "paginated"(hilo creado por TUI) →thread/readconincludeTurnsfalla (list_turns is not supported yet). Actualmente, recuperar el historial a posteriori por esta vía está roto; la respuesta depende del flujo en tiempo real.La aprobación se difunde a todos los clientes, y el primero en responder es el que cuenta (confirmado en macOS). El cliente que permanece en silencio también recibe la solicitud y luego recibe
serverRequest/resolved, el turno no se bloquea. En máquinas donde el sandbox helper de OS no se inicia (ciertos Windows empresariales gestionados pueden mostrarShellExecuteExW failed to launch setup helper: 1223), la escritura de archivos falla antes de "preguntar a la persona", por lo que la solicitud de aprobación nunca se emite, y esos tres elementos se marcan como SKIP — una limitación del entorno, no un problema del protocolo.El hook
PermissionRequesta nivel de usuario intercepta toda la solicitud de aprobación, el cliente no recibe ninguna (probado en macOS). Para más detalles, consulta la sección "Aprobación" más arriba.
Aún no validado:
turn/steer(intervenir en un turno en ejecución) solo se ha leído el esquema, no se ha probado.-Cno se probó→ Ya probado: cuando el TUI se conecta sin-C, el cwd del hilo es el cwd del app-server, independientemente del directorio desde el que ejecutescodex --remote; al usar-C <dir>, se cambia a ese directorio. (macOS, usando pty para iniciar el TUI y otro cliente para leerthread/read).Todo el protocolo está marcado como
[experimental], y puede cambiar con las actualizaciones de codex.
Referencias
Por qué la dirección inversa usa un Stop hook en lugar de otro mecanismo:
docs/reverse-channel.md
Esquema del protocolo: codex app-server generate-json-schema --out <dir>
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceThe self-hosted MCP bridge between Claude Chat and Claude Code.46AGPL 3.0
- AlicenseNot gradedqualityDmaintenanceBridges Claude Code and Google's Gemini AI models to enable AI-to-AI collaboration for code reviews, brainstorming, and direct questions.5MIT
- AlicenseNot gradedqualityDmaintenanceConnects Claude Desktop and Claude Code, enabling autonomous exchange of messages, files, and code while keeping their context windows separate.4MIT
- AlicenseAqualityAmaintenanceBridges 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.74753MIT
Related MCP Connectors
Stop copy-pasting between Claude Chat and Claude Code.
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
Trade Robinhood through natural language in Claude Code.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ar36planet/claude-codex-bridge-public'
If you have feedback or need assistance with the MCP directory API, please join our Discord server