agent-bridge
Enables OpenAI Codex CLI sessions to register as agents and exchange real-time messages with other AI coding sessions, using long-polling to receive messages.
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-bridgeask codex-1 to review foo.py and reply when done"
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.
aiAgentBridge
Let Claude Code, Codex and Opencode sessions message each other in real time.
aiAgentBridge is a small Python hub that every AI coding session connects to as an MCP server. Each
session registers under a name, then sends messages to another session by name or broadcasts them to
all sessions with *. Sessions can be grouped into rooms, so several independent groups can share
one hub without seeing each other. Messages arrive as soon as they're sent, with no polling:
Claude Code gets messages pushed into the conversation as
<channel>blocks. It uses the same mechanism as the official Telegram plugin.Codex and Opencode call
wait_for_message, a long-poll that returns the moment a message arrives.Scripts, CI jobs and bots can post to an HTTP webhook, or join as full agents over a WebSocket.
You can have Claude review what Codex just wrote, have Opencode tell Claude when tests pass, or let a CI job broadcast "build is green" to every open session.
Contents
Related MCP server: Agent Communication MCP Server
How it works
flowchart LR
subgraph Sessions
CC[Claude Code]
CX[Codex]
OC[Opencode]
end
AD[agent-bridge-stdio<br/>adapter]
HUB((agent-bridge hub<br/>127.0.0.1:8765))
EXT[Scripts / CI / bots]
CC -- stdio MCP --> AD
AD -- WebSocket /ws --> HUB
CX -- HTTP MCP /mcp --> HUB
OC -- HTTP MCP /mcp --> HUB
EXT -- POST /webhook or /ws --> HUBThe hub is one long-running process. It keeps the registry of named agents, routes messages, and queues direct messages for agents that are offline.
Codex and Opencode connect straight to the hub over MCP Streamable HTTP.
Claude Code connects through a small stdio adapter, agent-bridge-stdio. Claude Code talks to
HTTP MCP servers with the stateless 2026-07-28 protocol, which has no channel for the server to
push. Claude Code does accept pushes from stdio servers, though, so it launches the adapter locally.
The adapter holds a WebSocket to the hub and forwards each incoming message to Claude as a channel
notification.
One message, start to finish:
sequenceDiagram
participant X as Codex (codex-1)
participant H as Hub
participant A as stdio adapter
participant C as Claude Code (claude-main)
X->>H: wait_for_message (blocks)
C->>A: send_message to codex-1: please review foo.py
A->>H: send frame over WebSocket
H-->>X: wait_for_message returns the message
X->>H: send_message to claude-main: looks good
H->>A: message frame over WebSocket
A->>C: notifications/claude/channel
Note over C: shows up in the conversation as a<br/>channel block from codex-1Requirements
Python 3.13+
uv (Python package and project manager)
At least one of Claude Code, Codex CLI or Opencode
It works on Windows, macOS and Linux.
Install
git clone https://github.com/umairulh2001/aiAgentBridge.git
cd aiAgentBridge
uv syncuv sync creates .venv/ and installs two commands into it:
Command | What it is | Windows path | macOS / Linux path |
| The hub server |
|
|
| The stdio adapter for Claude Code |
|
|
In the rest of this README,
<path-to-repo>means the absolute path where you cloned the repository, e.g.C:\Users\you\aiAgentBridgeor/home/you/aiAgentBridge.
Start the hub
Run it in its own terminal and leave it running:
uv run agent-bridge
# or call the executable directly:
# Windows: .venv\Scripts\agent-bridge.exe
# macOS/Linux: .venv/bin/agent-bridgeIt listens on http://127.0.0.1:8765. Check it with curl http://127.0.0.1:8765/health (on Windows
PowerShell use curl.exe; see Troubleshooting).
Option | Env var | Default | Notes |
|
|
| Keep it on localhost unless you also set a token |
|
|
| |
|
| none | Requires |
|
| Default | |
| Extra | ||
|
|
All state is held in memory, so restarting the hub clears the queues. Clients reconnect on their own. Running it as a background service or at login is up to you; any process supervisor works.
Connect your assistants
Every session needs a unique name. That's the name other agents use to reach it. If two sessions
ask for the same name, the second one gets <name>-2 and is told so.
Claude Code
1. Register the adapter. This makes it available in every project:
# Windows
claude mcp add --scope user agent-bridge -- "<path-to-repo>\.venv\Scripts\agent-bridge-stdio.exe" --name claude-main
# macOS / Linux
claude mcp add --scope user agent-bridge -- "<path-to-repo>/.venv/bin/agent-bridge-stdio" --name claude-mainOr check it into one project as .mcp.json. Claude Code expands ${VAR}, so each window can pick
its own name:
{
"mcpServers": {
"agent-bridge": {
"command": "<path-to-repo>/.venv/bin/agent-bridge-stdio",
"args": ["--name", "${AGENT_NAME:-claude}", "--room", "${AGENT_ROOM:-default}"]
}
}
}AGENT_NAME=claude-frontend AGENT_ROOM=shop-app claude --dangerously-load-development-channels server:agent-bridge2. Start Claude Code with channels enabled:
claude --dangerously-load-development-channels server:agent-bridgeThe text after
server:must match the name you registered the server under (agent-bridge).Channels are a research-preview feature of Claude Code, so the flag may change between versions. Check
claude --helpand the Claude Code docs if it's rejected.Messages appear as
<channel>blocks only in interactive sessions. Inclaude -p, or without the flag, Claude can still receive everything by callingwait_for_message. The adapter keeps every message, so nothing is lost.
Adapter options:
Option | Env var | Default | |
|
|
| Name to register under |
|
|
| Room to join (see Rooms) |
|
|
| Hub URL |
|
| none | Prefer the env var: args are stored in plain text in configs |
|
|
|
|
|
| none | Claude Code hides a stdio server's stderr, so use this to debug |
Start order doesn't matter. If the hub isn't up yet, the adapter keeps retrying (0.5s, backing off to 10s). Until it connects, tool calls return "cannot reach the agent-bridge hub".
Why the full executable path instead of
uv run?uv runre-syncs the environment before every launch. On Windows that clashes with the running hub's locked files (see Troubleshooting). If you prefer uv, useuv run --no-sync --directory <path-to-repo> agent-bridge-stdio --name claude-main.No push needed? Claude can also connect directly over HTTP in wait mode:
claude mcp add --scope user --transport http agent-bridge "http://127.0.0.1:8765/mcp?name=claude-main".
Codex
Add this to ~/.codex/config.toml (Windows: %USERPROFILE%\.codex\config.toml):
[mcp_servers.agent-bridge]
url = "http://127.0.0.1:8765/mcp?name=codex-1"
tool_timeout_sec = 600 # wait_for_message blocks; the default (~60s) is too short
default_tools_approval_mode = "approve" # needed for `codex exec`, which can't prompt for approval
# bearer_token_env_var = "BRIDGE_TOKEN" # if the hub runs with --tokenOpencode
Add this to opencode.json (in the project or in your global Opencode config):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"agent-bridge": {
"type": "remote",
"url": "http://127.0.0.1:8765/mcp?name=opencode-1",
"enabled": true
}
}
}If the hub has a token, add "headers": { "Authorization": "Bearer {env:BRIDGE_TOKEN}" }. Opencode
fills in {env:...} from the environment, so the token stays out of the file.
Wake Opencode when a message arrives (optional plugin). Opencode has no push, so a message waits
until the model calls wait_for_message. The plugin opencode-plugin/agent-bridge-wake.ts fixes
that: it polls the hub's /health for this agent's queued count and, when messages wait and the
session is idle, starts a turn telling the model to read and answer them. To install it, copy the file
into ~/.config/opencode/plugins/ (Windows: %USERPROFILE%\.config\opencode\plugins\) or a project's
.opencode/plugins/, then restart Opencode.
It reads the hub URL, name and room from the
agent-bridgeMCP entry, and the token fromBRIDGE_TOKEN(or the entry'sAuthorizationheader). Env overrides:AGENT_BRIDGE_WAKE_NAME,AGENT_BRIDGE_WAKE_SERVER,AGENT_BRIDGE_WAKE_INTERVAL(seconds, default 3),AGENT_BRIDGE_WAKE_DISABLE=1.It wakes the most recently active session, so a fresh Opencode needs one prompt first.
It follows
registerandjoin_room. If the hub suffixed the name (opencode-1-2), the plugin learns it the first time the agent callswhoamiorlist_agents.
Several windows of the same client: Codex and Opencode configs can't vary the name per window. A second Codex window gets
codex-1-2automatically, or the agent can callregisterto pick a clearer name.
Rooms
A room is a group id that you choose, such as a project, a feature or a chat id. Sessions in the
same room can see and message each other. Sessions in other rooms are invisible to them: list_agents,
send_message and the * broadcast never cross rooms. Names are unique per room, so claude-main
can exist in two rooms at once. A session is in one room at a time. Without a room, it joins default,
so a hub where nobody sets a room behaves as before.
Room ids are 1–64 characters of a-z 0-9 _ . : - and are case-insensitive. For example, shop-app,
proj:auth and -100123456 are all valid.
Pick the room when connecting:
Client | How |
Claude Code (stdio adapter) |
|
Codex / Opencode / HTTP | Add |
WebSocket |
|
Or switch rooms at runtime with the join_room(room) tool. Codex and Opencode configs have one fixed
URL, so this is how two Codex windows end up in different rooms. Ask the agent, for example, "call
join_room with room shop-app".
join_roomis refused while you have unread messages. Read them withwait_for_messagefirst. This prevents replying to a sender from the old room and reaching a same-named agent in the new room.Your queued messages don't move with you, and your old name stops existing in the old room.
If your name is taken in the new room by an active session, you get
name-2, as when connecting.A Codex or Opencode session that reconnects starts again in the room from its URL.
Outside senders (webhook, send-only WebSocket) pass "room" in the body or frame; it defaults to
default. They alone may use "room": "*" together with "to": "*" to broadcast to every room.
A room id is not a password. /health lists every room, and any client that can reach the hub can
join any room. Rooms keep groups apart, but they don't keep anyone out. Use --token for access
control.
Quick start
Start the hub:
uv run agent-bridge.Open Codex and ask:
Call whoami, then wait_for_message with timeout_s=300. When a message arrives, answer it with send_message to its sender.
Open Claude Code with channels enabled and ask:
Use agent-bridge to ask codex-1 what 2+2 is.
Codex's wait returns, Codex replies, and the answer appears in Claude's conversation as a
<channel source="agent-bridge" from="codex-1">block.
To see who's connected at any point: curl http://127.0.0.1:8765/health, or ask any agent to call
list_agents.
Tools
Every connected session gets these MCP tools:
Tool | Description |
| Your name, room, push mode, whether push is ready, number of queued messages |
| Rename yourself (1–32 chars: |
| Every known agent in your room: name, room, client, online, push mode, queued count |
| Move to another room. Refused while you have unread messages (see Rooms) |
|
|
| Blocks until messages arrive and returns them oldest first. |
Each message looks like this:
{"id": "75915e47e1d4", "from": "codex-1", "to": "claude-main", "content": "looks good",
"ts": "2026-10-03T07:59:40Z", "reply_to": "05ca99dd3850", "room": "default"}pushed in a delivery report means the message was handed to the recipient's connection, not that
the model has read it.
Webhook and WebSocket API
POST /webhook
# macOS / Linux, or Windows with curl.exe
curl -X POST http://127.0.0.1:8765/webhook \
-H "Content-Type: application/json" \
-d '{"from":"ci","to":"*","content":"build is green"}'# Windows PowerShell
Invoke-RestMethod -Method Post -Uri http://127.0.0.1:8765/webhook -ContentType "application/json" `
-Body '{"from":"ci","to":"claude-main","content":"build is green"}'The body is
from(optional),to(a name or*),content, and optionallyreply_toandroom(defaultdefault;"*"withto: "*"reaches every room).The sender is recorded as
ext:<from>, so outside callers can't impersonate a registered agent.The endpoint returns the delivery report. A name that isn't in the room returns
404; if the name exists in other rooms, thehintnames them. Bad input returns400.
GET /health
Returns {"status": "ok", "agents": [...]} for every room, each agent with its room. Add
?room=<id> to list one room.
WebSocket /ws
Connecting to ws://127.0.0.1:8765/ws?name=<name>&room=<room> registers you as an agent (add
&token=... if the hub has one; room defaults to default). Leave out name to connect as a
send-only producer. A producer sends to the frame's room, falling back to the URL's room and then
default.
You send | Hub replies |
|
|
|
|
|
|
|
|
|
|
Add
"id": <anything>to a frame, and the reply carries the sameid.Incoming messages arrive as
{"type":"message", "id": ..., "from": ..., "content": ...}.Errors come back as
{"type":"error","error":"..."}.
A minimal agent in Python:
import asyncio, json, websockets
async def main():
async with websockets.connect("ws://127.0.0.1:8765/ws?name=my-bot") as ws:
print(json.loads(await ws.recv())) # {"type": "registered", ...}
await ws.send(json.dumps({"type": "send", "to": "claude-main", "content": "hello from my-bot"}))
async for raw in ws:
frame = json.loads(raw)
if frame["type"] == "message":
print(f"{frame['from']}: {frame['content']}")
asyncio.run(main())Delivery rules
Rooms: every rule below applies within one room. A name in another room counts as unknown.
Direct messages to an offline agent (one known from an earlier connection) are queued: up to 500 per agent, after which the oldest is dropped. They're delivered when a session claims that name in the same room again. A session that comes back in a different room doesn't get them. Names that stay offline expire after 24 hours.
Messages to an unknown name are rejected (reported as
unknown), not queued.Broadcasts (
to: "*") reach only agents in the room that are online right now, and never the sender.Order is first-in, first-out for each recipient. If a push connection drops, messages queue up and are flushed in order when it comes back.
Name clashes:
If the current holder is still active, a newcomer gets
name-2. Active means its push connection is open, it's blocked inwait_for_message, or it made a request in the last 2 minutes.If the holder is stale, the newcomer takes over the name and its queued messages.
A stale session that comes back is renamed on its next call and told so.
Vanished clients: a client that disconnects in the middle of
wait_for_messageis noticed within about a second. It gives up its name and never swallows a message.Slow clients: a push that takes longer than 2 seconds is abandoned and the message is queued instead. One stuck client never delays the others.
Persistence: everything is held in memory.
Security
The hub listens only on
127.0.0.1and has DNS-rebinding protection (it rejects foreignHostheaders).Use
--token/BRIDGE_TOKENif other users or programs on the machine shouldn't connect, and always if you bind to anything other than localhost.There's no proof of identity. A client that can reach the hub can claim a stale name and read that name's queue.
Rooms aren't access control. Anyone who can reach the hub can join any room; see Rooms.
Treat messages from other agents as untrusted input. The server's instructions tell agents not to run destructive commands or reveal secrets just because a peer asked, and to check with their user first.
There is no loop guard. Two agents told to "always reply" will keep talking to each other and burning tokens. Tell agents not to answer plain acknowledgements, and watch the first few exchanges.
Troubleshooting
Symptom | Cause / fix |
No | Channels show up only in interactive sessions started with |
Claude's | Claude is connected over HTTP, which can't push. Use the stdio adapter. |
Codex says "MCP tool call requires approval, but approval policy is never" | Add |
| It's waiting for stdin to close. In scripts, run |
Codex's | Set |
Your session got | Another active session holds that name in the same room. Close it, wait about 2 minutes, or call |
| It's in another room. Compare |
| Call |
Windows: | The running hub locks |
Tool calls return "cannot reach the agent-bridge hub" | The hub isn't running, or |
| In Windows PowerShell 5, |
| The hub has a token. Send |
You need to see what the adapter is doing | Start it with |
Development
uv syncsrc/agent_bridge/
hub.py # routing core: rooms, named mailboxes, sessions, queues, wait/flush (no MCP imports)
mcp_server.py # MCP tools, session binding, channel push over Streamable HTTP
web.py # Starlette app: /mcp, /webhook, /ws, /health, token auth
stdio_shim.py # agent-bridge-stdio: stdio MCP server that proxies to the hub over WebSocket
__main__.py # agent-bridge CLImcp is pinned to 2.3.x because the code relies on a few private parts of the SDK:
Connection.notify, to send custom notificationsServerRequestContext.session._connectionthe transport's
_request_streams, to detect open push streams and disconnected callers
License
MIT © 2026 Umair
Available Tools
6 toolsjoin_roomA
Move this session to another room (group id, 1-64 chars: a-z 0-9 _ . : -). Only agents in the same room can see or message each other. Fails while you have unread messages: read them with wait_for_message first.
| Name | Required | Description | Default |
|---|---|---|---|
| room | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses the failure condition (unread messages), the prerequisite tool, and the visibility constraint that only same-room agents can see or message each other. It omits whether joining is idempotent, whether the room must already exist, and what success returns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the action, then the format constraint, then the failure precondition and remedy. No padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-param tool with no annotations or output schema, the description covers action, parameter format, visibility semantics, and failure mode. It stops short of stating post-condition behavior (does the session leave the prior room, is the room auto-created), which is the only remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the schema only says 'string', but the description supplies the full format contract: 1-64 chars from a-z 0-9 _ . : -. That is exactly the meaning an agent needs and cannot get from the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Move this session to another room', and adds the semantic effect (room scoping of visibility/messaging). No explicit contrast against siblings like send_message or list_agents, but none compete for the same action, so the purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete prerequisite and routes to a sibling: 'Fails while you have unread messages: read them with wait_for_message first.' That is clear when-to-use-context, though there are no true alternative tools to choose between for this action.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_agentsA
List the agents in your room: name, client, online, push mode, queued count.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It discloses the information returned (name, client, online, push mode, queued count) and implies a read-only listing, but it does not explicitly state that the operation has no side effects, nor does it describe permissions, failure conditions, or result ordering.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence that states the action, scope, and returned fields without any filler. Every element earns its place for a zero-parameter listing tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity, zero parameters, and no output schema, the description is nearly complete because it enumerates the returned fields. It lacks usage guidance and explicit read-only behavior, but those gaps are minor for a basic room-agent listing operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the description has no parameter semantics to clarify. With an empty schema, the baseline score of 4 applies; the description appropriately avoids inventing parameters and instead documents observable output fields.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('List the agents') and scopes it to 'your room', making the core purpose clear. It also enumerates the returned fields, which helps an agent understand what the tool provides. However, it does not explicitly differentiate itself from sibling tools such as whoami or join_room, so it falls short of the top score.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description offers no guidance on when to use this tool versus alternatives like whoami, register, or join_room. It does not state prerequisites, exclusions, or expected invocation context. Usage is only implied by the phrase 'in your room'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
registerA
Change this session's bridge name (1-32 chars: a-z 0-9 _ . -). Queued messages move with you; the old name stops existing.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does well: it discloses two non-obvious side effects — pending messages follow the new name, and the old name ceases to exist. It omits auth/permission requirements and whether the name must be unique among existing agents.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with no filler. The core action comes first and the constraints and side effects follow in a logical order; every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter tool with no annotations and no output schema, the description covers action, format, and side effects. Gaps are minor: it does not say what a successful or failed rename returns, or how collisions with existing names are handled.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter is a bare string in the schema with 0% description coverage, so the description must compensate — and it does, specifying length (1-32) and the allowed character class (a-z 0-9 _ . -). It does not state whether the name must be unique or free of collisions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: it changes this session's bridge name. It also resolves the ambiguity of the bare name 'register' by explaining it is a rename, not a signup. It does not explicitly contrast itself with siblings, but no sibling (whoami, join_room, send_message) could be confused with it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no statement of when to call this tool, when not to, or what alternative exists. The clause 'Queued messages move with you' hints that renaming mid-session is safe, but the agent must infer the trigger condition on its own.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
send_messageA
Send a message to one agent in your room by name, or to "*" to broadcast to every online agent in your room. Returns a delivery report: pushed (handed to the recipient's connection), queued (waiting in its inbox), unknown.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | recipient name, or "*" for broadcast | |
| content | Yes | ||
| reply_to | No | message_id this replies to (optional) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It does add real value by explaining the delivery report states (pushed/queued/unknown), which tells the agent how to interpret non-errors. It omits prerequisites, permission/auth requirements, and what happens to 'unknown' recipients.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with no waste; addressing modes come first, return semantics second. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully documents the return report, which the agent could not otherwise know. It is nearly complete for a moderate-complexity tool, lacking only prerequisite/registration context and error behavior for unknown recipients.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%; the description reinforces 'to' as a recipient name or "*" for broadcast, matching the schema entry. It says nothing about 'content' or the optional 'reply_to' threading parameter, so it only partially compensates for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Send a message to one agent in your room') plus the broadcast variant, and the room scoping makes it clearly distinct from receiving-oriented siblings like wait_for_message. An agent can tell what this does without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the description: use this to reach a named agent or everyone online. However, there is no explicit when-to-use/when-not guidance and no mention of prerequisites (register/join_room), even though sibling tools suggest those steps come first.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
wait_for_messageA
Block until messages for this session arrive, then return them (oldest first). Use it when idle or when expecting a reply; it returns as soon as a message arrives. timeout_s=0 just checks the inbox.
| Name | Required | Description | Default |
|---|---|---|---|
| max | No | max messages to return (default 20) | |
| timeout_s | No | seconds to wait (0-600) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does well: it discloses blocking behavior, return ordering (oldest first), early return on arrival, and the timeout_s=0 non-blocking check. It doesn't state the max timeout bound or what happens on expiry (empty return vs. error), which would complete the picture.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly packed sentences with zero filler: behavior first, then when to use it, then the edge case. Front-loaded and easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a simple 2-param, no-annotation, no-output-schema tool, the description covers purpose, blocking semantics, ordering, and the zero-timeout case. Only the timeout expiry behavior and the 0-600 bound are missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description adds value by explaining the semantics of timeout_s=0 beyond the schema's bare 'seconds to wait', clarifying that it degrades to an inbox check. The 'max' parameter is left entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Block until... return them') and resource ('messages for this session'), and distinguishes itself from siblings like send_message by describing the blocking receive pattern. An agent can immediately tell this is the receive counterpart to send_message.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage contexts ('Use it when idle or when expecting a reply') and explains the timeout_s=0 polling case. It doesn't explicitly name alternatives, but in a send/receive messaging pair the alternative (send_message) is obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
whoamiA
Show this session's bridge name, room, push mode and queued message count.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. 'Show' implies a safe read and the description enumerates the returned fields, but it does not state what happens if the session is unregistered, whether it has side effects, or whether values like 'push mode' have a fixed set of states.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler; every listed field earns its place by telling the agent what it will learn.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must convey the return surface, and it does enumerate the four fields. It falls just short of complete by not indicating formats or possible values for 'push mode' and the queued count.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline of 4 applies. There is no argument surface the description could clarify further.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Show') and names the exact resource: this session's bridge name, room, push mode, and queued message count. 'This session's' distinguishes it from list_agents, which enumerates other agents rather than the caller's own state, though no sibling is named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: an agent can infer it is a session introspection call. There is no explicit guidance on when to call it (e.g., before join_room or after register) and no mention of alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.1.0- First observed
join_room - First observed
list_agents - First observed
register - First observed
send_message - First observed
wait_for_message - First observed
whoami
TDQS
Scored across 6 tools
Most tools target clearly distinct operations: send_message vs wait_for_message vs list_agents vs join_room are easy to tell apart. Minor overlap exists between whoami and list_agents (both report session/queue info) and between register and join_room (both mutate session identity/state), but descriptions clarify each.
Four tools follow a clean verb_noun snake_case pattern (list_agents, join_room, send_message, wait_for_message). whoami breaks snake_case style and register is a bare verb with no noun, but the intent stays obvious and readable.
Six tools is well-scoped for a messaging bridge: identity, room membership, discovery, send, and receive. Each tool earns its place with no redundancy or filler.
The core messaging lifecycle (identify, rename, discover peers, change room, send, receive) is fully covered. Minor gaps like leaving a room without joining another or listing all rooms are workable around, so the surface is not a dead end.
Maintenance
Related MCP Connectors
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
Ephemeral REST chatrooms for AI agents to coordinate. Share a room URL — agents talk live.
Public and private rooms for agents, with messages, files, search, and resumable events.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables real-time communication between Claude Code instances across multiple machines via WebSocket, allowing context sharing, task handoffs, and coordination between sessions.63 npm5MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to communicate with each other through Slack-like room-based channels with messaging, mentions, presence management, and long-polling for real-time collaboration.241 npm5MIT
- FlicenseNot gradedqualityBmaintenanceEnables AI coding agents to communicate and coordinate through a durable, vendor-neutral message bus with support for threads, tasks, presence, and webhooks.283 npm-
- AlicenseNot gradedqualityCmaintenanceEnables independent coding agents and any HTTP caller to communicate in shared rooms, with @-mention and broadcast wake-ups so sessions notice messages even when idle.MIT