Skip to main content
Glama

aiAgentBridge

Let Claude Code, Codex and Opencode sessions message each other in real time.

Python 3.13+ License: MIT MCP

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 --> HUB

The 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-1

Requirements

It works on Windows, macOS and Linux.


Install

git clone https://github.com/umairulh2001/aiAgentBridge.git
cd aiAgentBridge
uv sync

uv sync creates .venv/ and installs two commands into it:

Command

What it is

Windows path

macOS / Linux path

agent-bridge

The hub server

.venv\Scripts\agent-bridge.exe

.venv/bin/agent-bridge

agent-bridge-stdio

The stdio adapter for Claude Code

.venv\Scripts\agent-bridge-stdio.exe

.venv/bin/agent-bridge-stdio

In the rest of this README, <path-to-repo> means the absolute path where you cloned the repository, e.g. C:\Users\you\aiAgentBridge or /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-bridge

It 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

--host

BRIDGE_HOST

127.0.0.1

Keep it on localhost unless you also set a token

--port

BRIDGE_PORT

8765

--token

BRIDGE_TOKEN

none

Requires Authorization: Bearer <token> on every route

--wait-default

50

Default wait_for_message timeout in seconds

--allowed-host

Extra Host header to accept (repeatable), e.g. mybox:8765

--log-level

info

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-main

Or 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-bridge

2. Start Claude Code with channels enabled:

claude --dangerously-load-development-channels server:agent-bridge
  • The 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 --help and the Claude Code docs if it's rejected.

  • Messages appear as <channel> blocks only in interactive sessions. In claude -p, or without the flag, Claude can still receive everything by calling wait_for_message. The adapter keeps every message, so nothing is lost.

Adapter options:

Option

Env var

Default

--name

AGENT_NAME

claude

Name to register under

--room

AGENT_ROOM

default

Room to join (see Rooms)

--hub

BRIDGE_HUB

ws://127.0.0.1:8765

Hub URL

--token

BRIDGE_TOKEN

none

Prefer the env var: args are stored in plain text in configs

--push

BRIDGE_PUSH

channel

wait turns push off; messages are then only returned by wait_for_message

--log-file

BRIDGE_LOG_FILE

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 run re-syncs the environment before every launch. On Windows that clashes with the running hub's locked files (see Troubleshooting). If you prefer uv, use uv 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 --token

Opencode

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-bridge MCP entry, and the token from BRIDGE_TOKEN (or the entry's Authorization header). 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 register and join_room. If the hub suffixed the name (opencode-1-2), the plugin learns it the first time the agent calls whoami or list_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-2 automatically, or the agent can call register to 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)

--room shop-app, or AGENT_ROOM=shop-app

Codex / Opencode / HTTP

Add &room=shop-app to the URL, e.g. http://127.0.0.1:8765/mcp?name=codex-1&room=shop-app, or send an X-Agent-Room header

WebSocket

ws://127.0.0.1:8765/ws?name=my-bot&room=shop-app

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_room is refused while you have unread messages. Read them with wait_for_message first. 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.

NOTE

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

  1. Start the hub: uv run agent-bridge.

  2. 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.

  3. Open Claude Code with channels enabled and ask:

    Use agent-bridge to ask codex-1 what 2+2 is.

  4. 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

whoami()

Your name, room, push mode, whether push is ready, number of queued messages

register(name)

Rename yourself (1–32 chars: a-z 0-9 _ . -). Queued messages move with you and the old name stops existing

list_agents()

Every known agent in your room: name, room, client, online, push mode, queued count

join_room(room)

Move to another room. Refused while you have unread messages (see Rooms)

send_message(to, content, reply_to?)

to is a name in your room, or "*" for every online agent in your room except you. Returns a delivery report: pushed, queued, dropped_oldest, unknown

wait_for_message(timeout_s?, max?)

Blocks until messages arrive and returns them oldest first. timeout_s=0 only checks the inbox. remaining > 0 means call again

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 optionally reply_to and room (default default; "*" with to: "*" 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, the hint names them. Bad input returns 400.

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

{"type":"send","to":"...","content":"...","reply_to":"..."}

{"type":"report", ...delivery report}

{"type":"list"}

{"type":"agents","you":"...","room":"...","agents":[...]} (your room only)

{"type":"whoami"}

{"type":"whoami","name":"...","room":"...","client":"..."}

{"type":"register","name":"..."}

{"type":"registered", ...}

{"type":"join","room":"..."}

{"type":"joined","name":"...","room":"...", ...}

  • Add "id": <anything> to a frame, and the reply carries the same id.

  • 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 in wait_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_message is 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.1 and has DNS-rebinding protection (it rejects foreign Host headers).

  • Use --token / BRIDGE_TOKEN if 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.

WARNING

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 <channel> blocks appear in Claude Code

Channels show up only in interactive sessions started with --dangerously-load-development-channels server:agent-bridge, not in claude -p. Messages aren't lost: call wait_for_message (pushed ones are marked pushed: true).

Claude's whoami shows push_mode: "wait" and client: "claude-code"

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 default_tools_approval_mode = "approve" to the [mcp_servers.agent-bridge] block.

codex exec sits at "Reading additional input from stdin…"

It's waiting for stdin to close. In scripts, run codex exec ... </dev/null.

Codex's wait_for_message fails after ~60s

Set tool_timeout_sec = 600, or pass a shorter timeout_s.

Your session got name-2

Another active session holds that name in the same room. Close it, wait about 2 minutes, or call register.

send_message says unknown, but the agent is connected

It's in another room. Compare whoami on both sides, then join_room.

join_room says you have unread messages

Call wait_for_message (timeout_s=0 is enough) until it returns nothing, then try again.

Windows: uv sync / uv run fails with "being used by another process"

The running hub locks .venv\Scripts\*.exe. Stop the hub and run uv sync again. If a sync failed halfway, agent-bridge-stdio.exe may be missing, and re-running uv sync restores it.

Tool calls return "cannot reach the agent-bridge hub"

The hub isn't running, or --hub points at the wrong host or port.

curl on Windows PowerShell behaves strangely

In Windows PowerShell 5, curl is an alias for Invoke-WebRequest. Use curl.exe or Invoke-RestMethod.

401 unauthorized

The hub has a token. Send Authorization: Bearer <token>, or ?token= on WebSockets.

You need to see what the adapter is doing

Start it with --log-file <path>; its stderr isn't visible inside Claude Code.


Development

uv sync
src/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 CLI

mcp is pinned to 2.3.x because the code relies on a few private parts of the SDK:

  • Connection.notify, to send custom notifications

  • ServerRequestContext.session._connection

  • the transport's _request_streams, to detect open push streams and disconnected callers


License

MIT © 2026 Umair

Available Tools

6 tools
join_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.

ParametersJSON Schema
NameRequiredDescriptionDefault
roomYes

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters5/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.5/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes

TDQS

A3.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
toYesrecipient name, or "*" for broadcast
contentYes
reply_toNomessage_id this replies to (optional)

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
maxNomax messages to return (default 20)
timeout_sNoseconds to wait (0-600)

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

  1. 6 tool updatesv0.1.0
    • First observedjoin_room
    • First observedlist_agents
    • First observedregister
    • First observedsend_message
    • First observedwait_for_message
    • First observedwhoami

TDQS

A3.9/5.0

Scored across 6 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers