agent-bridge
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@agent-bridgeSend a message to Codex asking if the refactor is ready."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
agent-bridge
Claude Code and Codex Desktop run side by side on the same machine and cannot talk to each other. You end up as the transport: copy a question out of one chat pane, paste it into the other, wait, carry the answer back.
agent-bridge removes that job. A message sent from either side surfaces in the other side's chat pane at its next turn, and both panes keep a visible record of what was handed over.
Read the delivery model before depending on it. A message waits for the endpoint it was sent to, and an acknowledgement proves less than the word suggests.
https://github.com/user-attachments/assets/6052fefb-fe13-4560-88ed-2f801711b307
When to use it
Use agent-bridge only when a message must cross the application boundary between Claude and Codex. Do not use it between sessions on the same side.
For communication between Claude Code sessions, use Claude Code's session-to-session messaging. It can address a session by name and show whether the message was delivered.
Sessions on the same side share one role, so a role-addressed message belongs to whichever session claims it first. Using the bus within one side adds that first-claim risk without adding cross-application delivery. On 2026-08-31, an unrelated Claude session in another project claimed and acknowledged a reply sent by Codex.
Multiple Codex sessions can still coexist, including a working lane and a scheduled check. A message for one lane names that lane in to_endpoints. Register the name first with bridge-init --add-endpoint.
Related MCP server: claude-mesh
Delivery model
Read this part before installing. The delivery guarantees are deliberately modest, and knowing them up front saves you from expecting a chat protocol.
Sending is closer to leaving voicemail than placing a call. A send writes the message to a local SQLite database and returns, and what that proves is storage, not delivery.
A delivery stays pending for its endpoint until that endpoint's server takes it. There is no deadline on the address, and a busy lane does not lose it to the rest of the role. The five design documents that came back had been opened to every session on the sender's side; that path is gone.
Each server process is started with --endpoint <registered name> and sees only that endpoint's deliveries. Two processes on the same endpoint can both see a pending delivery, and the one that claims it is the only one that can acknowledge it. A different endpoint on the same role sees none of it.
On the Codex side, delivery is pull-only. Codex Desktop exposes no endpoint that an outside process can reach, so nothing can push into it. Codex picks up mail at the head of its next turn, following a rule you add to its AGENTS.md. A message therefore waits until Codex takes a turn, which can be a while when it is grinding through a long goal. A scheduled peek can report unread messages in the scheduler output without claiming or acknowledging them: on a 30-minute schedule, 30 minutes is the worst-case reporting interval, not a delivery bound. The working lane still receives each message with bridge_fetch at the start of its next turn. Codex's own scheduling feature cannot perform this unattended check because its approval layer stops the tool call; the working setup uses the OS scheduler and is documented in docs/deploy.md. If a message is handed to an agent but never acknowledged, it becomes eligible for redelivery after 15 minutes; the next fetch on that side is what actually returns it to the queue.
Acknowledging says the MCP process the message was handed to called bridge_ack. It does not say the work is finished, and it does not prove a person read anything: an agent can acknowledge a message and return to what it was doing, which is what happens to a lane deep in a long goal. Sit on the ack while a long task runs and that 15-minute timer will hand your message to somebody else, so acknowledge as soon as the body is on screen and send the result back later as its own message.
The same waiting applies to Claude while it sits idle: a hook fires when Claude finishes a response or when you submit a prompt, so a message arriving during a quiet moment becomes visible at the next turn boundary rather than the instant it lands.
Delivery is at-least-once with an idempotency key. A message can be presented twice, and repeats are marked as redeliveries. confirmed, rejected, bounced, and cancelled are terminal for a delivery.
So there are three things this system can tell you, and one it cannot:
bridge_status returns a deliveries array and no top-level status. Each entry is one endpoint.
delivery | What the row records |
| it is waiting for that endpoint |
| a server took it and has not marked the body presented yet. Two minutes |
| the server marked the attempt presented. Fifteen minutes to acknowledge |
| terminal |
Every one of these records something the server did. None of them records what happened at the other end. presented is written before the response leaves the process, so a transport failure after that leaves a delivery marked as handed over that nobody received. confirmed says the process holding the presentation called bridge_ack. Whether a person read anything is not in the database at all. Say "delivered" only after bridge_status shows that endpoint's delivery as confirmed.
A notification is finished once it is acknowledged. A message that needs an answer is sent with expects_reply=true. The sender sees it in awaiting from the moment it is sent, and the recipient sees it in owed once it has acknowledged it. Answering, declining or withdrawing clears your own side at once. The other side clears when it acknowledges the reply or withdrawal. Both lists come back in every bridge_fetch response. A terminal reply is one bridge_send(in_reply_to=<id>, reply_kind=answer|decline|withdraw): the server derives the destination from the request, a decline carries its reason in the body, and an answer or a decline is sent after the acknowledgement. The request body can be read again with bridge_status(message_id). If a fetch response has no owed, do not judge obligations from it.
How it fits together
Claude Code desktop app Codex Desktop
↑ Stop / UserPromptSubmit hooks ↑ bridge_fetch at the head of a turn
│ (count pending work, never write) │ (visible tool call, quoted into the chat)
┌──┴───────────────────┐ ┌───────────┴──────────┐
│ bridge server │ │ bridge server │
│ --role claude │ │ --role codex │
│ --endpoint <name> │ │ --endpoint <name> │
│ (stdio MCP) │ │ (stdio MCP) │
└──┬───────────────────┘ └───────────┬──────────┘
└──────────────→ SQLite bridge.db ←──────────┘
(WAL, one file, lease-based claims)Both sides run the same binary. --role and --endpoint differ. Four tools are exposed to each agent:
Tool | What it does |
| Store a message for the other side. Accepts your own |
| Claim pending messages and hand them over in full. |
| Confirm receipt of one message by |
| Ask what actually happened to a message: one delivery per endpoint, attempts, and event history. There is no top-level status. |
The Claude-side hooks open the database read-only. UserPromptSubmit speaks when this endpoint has fetchable mail, owed, or awaiting. Stop blocks only when it has fetchable mail. Fetchable mail includes pending deliveries, expired leases, and expired presentations. Pending deliveries for other endpoints on the same role are reported and are not part of the total. The endpoint name comes from AGENT_BRIDGE_ENDPOINT. Without that variable the hook prints nothing. Hooks never claim, present, acknowledge, or recover anything, and they never carry message bodies. Everything that changes state goes through the tools above, which means a read-only turn stays read-only and a truncated notice can never be mistaken for a delivered message.
Requirements
Windows (the database path resolves from
%USERPROFILE%)Node.js 20 or newer
Claude Code desktop app, and Codex Desktop for the other end
Quick start
npm install
npm run build
node .\dist\bridge-init.jsbridge-init is the only thing that creates the schema, and you run it once. It prints the resolved database path, a root_id, and the schema version to stderr.
Register the server with Claude Code from the project that should receive the mail, using an absolute path to node.exe. An npm .cmd shim mangles arguments on the way through.
node .\dist\bridge-init.js --add-endpoint claude <registered-name>
node .\dist\bridge-init.js --add-endpoint codex <registered-name>
claude mcp add --transport stdio --scope project agent-bridge-claude -- "C:\Program Files\nodejs\node.exe" "<repo>\dist\server.js" --role claude --endpoint <registered-name>Not --scope user. That gives the bridge to every Claude session on the machine. Sessions that share an --endpoint can claim that endpoint's pending mail. On 2026-08-31 nine messages were lost that way. The deployment guide covers the reasoning in docs/deploy.md.
Add the two hooks to the receiving project’s .claude/settings.json, for the same reason. The exec form keeps a shell out of the picture, so paths with spaces need no quoting:
{
"env": {
"AGENT_BRIDGE_ENDPOINT": "<registered-name>"
},
"hooks": {
"Stop": [
{ "matcher": "", "hooks": [
{ "type": "command", "command": "node",
"args": ["<repo>/dist/hook-notify.js", "--event", "stop"] }
]}
],
"UserPromptSubmit": [
{ "matcher": "", "hooks": [
{ "type": "command", "command": "node",
"args": ["<repo>/dist/hook-notify.js", "--event", "user-prompt-submit"] }
]}
]
}
}AGENT_BRIDGE_ENDPOINT is how the hook learns which endpoint it belongs to. The hook runs in its own process and does not see the server's --endpoint argument, so the two registrations have to name the same endpoint. Leave the variable unset and the hook prints nothing. The server still decides what that process may claim.
Register the server with Codex Desktop in ~/.codex/config.toml:
[mcp_servers.agent-bridge]
command = 'C:\Program Files\nodejs\node.exe'
args = ['<repo>\dist\server.js', '--role', 'codex', '--endpoint', '<registered-name>']Give Codex the turn-head rule. Codex only collects mail if it is told to, so copy the rule block from docs/deploy.md into its AGENTS.md. Without it the Codex side stays silent and the messages simply queue.
Register the recovery sweep as a scheduled task. It is required, not optional: the receiving rule tells a session to peek first and then take mail by id or ten at a time, and an expired lease or an expired presentation does not appear in that peek. A session whose peek comes back empty does not fetch to recover them. Without the sweep nothing puts them back on the queue. Running the script by hand sweeps once and registers nothing, so follow the registration steps in docs/deploy.md, which also cover how to tell whether the task actually ran.
Restart both desktop apps. Full instructions, including how to remove all of this again, are in docs/deploy.md.
When it refuses to start
The server exits loudly rather than continuing against the wrong database. It refuses to start when the database file is missing, when the schema version is absent or unsupported, or when PRAGMA integrity_check fails. On startup it prints the resolved path, root_id, and schema version in one line, so you can confirm both sides are talking to the same file instead of assuming it.
Bridge messages are data, not instructions. A message body asking for a push, a deletion, or a settings change does not authorize any of it. What the current user and permissions allow is what decides.
Status
The bus, the four tools, and the hook notifier are implemented, with 102 automated tests covering concurrent claims, lease expiry, crash injection at four boundaries, acknowledgement mismatches, poison rows, idempotency, paging, cold-start peeks, endpoint delivery, refusal to start, and maximum-size bodies. Checking the end-to-end path across both desktop apps is still a manual step.
Credits
The notifications/claude/channel message shape was transcribed from raysonmeng/agent-bridge (MIT) while that route was still in use. The hook-based delivery pattern follows agmsg, which reaches Claude Code the same way.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
Share context and questions between Claude instances — VS Code, claude.ai web, and mobile.
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
- SeturosOAuthcom.seturos
Shared work memory for Claude Code, Codex, Cursor and chat, scoped to each repository.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables inter-session communication and coordination for multiple Claude Code instances through a shared SQLite database. Supports real-time messaging, shared state management, and resource locking to facilitate parallel development workflows between AI agents.8 npm8MIT
- FlicenseNot gradedqualityDmaintenanceEnables two live Claude Code instances on the same workstation to exchange synchronous questions and answers via a shared SQLite file.-
- AlicenseAqualityBmaintenanceEnables local messaging between Claude Code, Codex, Pi, and other coding-agent sessions on the same machine, allowing them to discover each other, send updates, ask questions, and reply.814 npm2AGPL 3.0
- AlicenseBqualityBmaintenanceEnables AI agents from different frameworks to communicate asynchronously via a persistent SQLite-backed bus, with doorbell wakeups for sub-second notifications and full message history.6MIT