AgentRadio MCP
# AgentRadio MCP (Python)
A Python [MCP](https://modelcontextprotocol.io) server that gives Cursor agents the AgentRadio primitives — `create_thread`, `send_message`, `wait_for_mention` — so agents in **different workspaces** can work on the same problem over a shared radio channel.
This is a Python port of the communication layer from [Coral-Protocol/AgentRadio](https://github.com/Coral-Protocol/AgentRadio) ([paper](https://arxiv.org/abs/2607.28430)). It does **not** run the Java `coral-server.jar` or the Harbor experiment harness. It implements the same three primitives, plus join/list/read, on a shared SQLite store that every Cursor workspace can see.
## Why this exists
AgentRadio's insight: agents should keep working while they listen. The original paper used a background `wait_for_mention` process. Cursor does not expose that watcher the same way, so this server:
1. Exposes the primitives as MCP tools Cursor agents can call.
2. Shares state across workspaces (one SQLite file, or one HTTP hub).
3. Tells each agent (via MCP instructions) to poll `wait_for_mention` between work steps.
Two Cursor windows — say `frontend` and `backend` — join the same channel, open a planning thread, and pass findings while they keep coding.
## Install
```bash
cd agentradio-mcp
python3 -m pip install -e .
```
Needs Python 3.10+ and `mcp` 1.21–1.x.
## Two ways to share a channel
### A. Same machine, multiple workspaces (simplest)
Each workspace runs its own stdio MCP process. They all open `~/.agentradio/radio.db`, so they see the same threads.
Put a **different** `AGENTRADIO_AGENT_ID` in each workspace's `.cursor/mcp.json`:
```json
{
"mcpServers": {
"agentradio": {
"command": "python3",
"args": ["-m", "agentradio_mcp"],
"env": {
"AGENTRADIO_AGENT_ID": "frontend",
"AGENTRADIO_CHANNEL": "my-app",
"AGENTRADIO_WORKSPACE": "web"
}
}
}
}
```
In the other workspace, use `"backend"` / `"api"`. Keep `AGENTRADIO_CHANNEL` the same.
Copy `cursor-rules/agentradio.mdc` into each workspace as `.cursor/rules/agentradio.mdc` so agents actually use the radio.
### B. One HTTP hub (best when many workspaces share one config)
Start a single process:
```bash
python3 -m agentradio_mcp --http --host 127.0.0.1 --port 8765
```
or `examples/start-hub.sh`.
Then every workspace (or your user-level `~/.cursor/mcp.json`) can use the same config:
```json
{
"mcpServers": {
"agentradio": {
"url": "http://127.0.0.1:8765/mcp"
}
}
}
```
Each agent calls `join_radio` with its own `agent_id`. That is how one shared MCP URL still has distinct identities.
Cursor Settings → MCP → add the server, then reload MCP.
## Tools
| Tool | What it does |
|---|---|
| `join_radio(agent_id, workspace?)` | Register this workspace on the channel. Call this first if `AGENTRADIO_AGENT_ID` is not set. |
| `list_agents` | Who is on the channel (and who is stale). |
| `create_thread(name, participants?)` | Open a named conversation. Empty participants = everyone currently joined. |
| `send_message(thread_id, content, mentions?)` | Append a message and return immediately. `@handles` in the text count as mentions. Mentioning someone adds them to the thread. |
| `wait_for_mention(timeout_ms=15000)` | Block until you are mentioned, any new visible message arrives, or timeout. Always returns a full state dump. |
| `read_state` | Snapshot of agents, threads, and messages you can see. |
| `leave_radio` | Mark this agent disconnected. History stays. |
Resources: `agentradio://state`, `agentradio://protocol`.
## How agents should work
1. `join_radio` as `frontend` / `backend` / `agent-1` / …
2. `list_agents` — wait for peers or start a thread they will join.
3. Keep working. Between steps, `wait_for_mention` (8–15s) or `timeout_ms=0`.
4. Share as you go. Prefix `FYI:` (no reply), `URGENT:` (handle now).
5. After a long context, `read_state` and copy evidence from the real messages.
Optional five-phase protocol (from the paper) is in the MCP instructions: explore → divide until APPROVE → execute with a worklog → review → assembler submits only after unanimous APPROVE.
## CLI
```bash
python3 -m agentradio_mcp # stdio (what Cursor launches)
python3 -m agentradio_mcp --http # hub at http://127.0.0.1:8765/mcp
python3 -m agentradio_mcp --dump-state frontend
```
| Env / flag | Default | Meaning |
|---|---|---|
| `AGENTRADIO_DB_PATH` / `--db` | `~/.agentradio/radio.db` | Shared SQLite file |
| `AGENTRADIO_CHANNEL` / `--channel` | `default` | Room name (isolate teams) |
| `AGENTRADIO_AGENT_ID` | unset | Auto-join this id on first tool call |
| `AGENTRADIO_WORKSPACE` | cwd basename | Label shown in `list_agents` |
| `AGENTRADIO_HOST` / `--host` | `127.0.0.1` | HTTP bind |
| `AGENTRADIO_PORT` / `--port` | `8765` | HTTP port |
## Tests
```bash
python3 -m pip install -e ".[dev]"
python3 -m pytest
```
## What this is not
- Not the SWE-Atlas / Harbor four-agent experiment runner.
- Not Coral Code (the product).
- Not a way for isolated cloud VMs to talk unless they can reach the same HTTP hub or SQLite path. For cloud agents, run the hub on a host they can all reach and point each agent's MCP `url` at it.
## License
Apache-2.0. See `LICENSE` and `NOTICE`.
TDQS
Scored across 7 tools
Each tool targets a distinct action (join, leave, list, create, send, wait, read_state). Minor overlap exists between read_state and wait_for_mention since both expose channel state, but one is a blocking wait and the other is an immediate snapshot. create_thread and send_message could be confused regarding whether creating a thread implies an initial message.
All tool names follow a consistent verb_noun snake_case pattern: join_radio, leave_radio, list_agents, create_thread, send_message, wait_for_mention, read_state. No mixed conventions or vague verbs.
Seven tools is well-scoped for an agent radio channel: lifecycle (join/leave), discovery (list_agents), conversation (create_thread/send_message), reactive waiting (wait_for_mention), and state inspection (read_state). Each tool earns its place without redundancy.
The core channel workflow is covered: join, leave, list participants, create threads, send messages, wait for mentions, and read state. Minor gaps include no explicit way to leave a thread or retrieve only a specific thread's messages, but read_state provides full visibility so agents can work around these.