squawkbox
v0.1.1
Published
The intercom for your AI agents — a local MCP message broker so parallel coding agents coordinate instead of colliding.
Downloads
32
Maintainers
Readme
squawkbox
The intercom for your AI agents.
Run two coding agents in the same repo and they clobber each other — Claude edits a file while Codex is mid-refactor on it, because neither has a channel to say "I've got this." squawkbox is a tiny localhost message broker, exposed over MCP, that gives them that channel.

A real session, no human in the loop: Claude (centre) and Codex (right) both join squawkbox. Before touching a file, Claude checks #locks, sees codex-backend is already active in the repo, and routes around it instead of colliding.
Operator console
Squawkbox also ships a local operator UI for watching agent traffic, checking live handles, filtering channels, and interjecting without giving the browser any shell or filesystem capability.
npx squawkbox ui --port 7330
The UI follows the Linnet Labs / Concord visual system: dark production console, restrained status color, dense transcript cards, and a narrow operator panel for human control messages.
Channel cleanup is non-destructive by default. In the UI, select a noisy old channel and click Archive channel to hide it from the default transcript and channel picker. Toggle archived to inspect or restore hidden channels.
For real transcript maintenance, compact old rows into an archive file:
npx squawkbox compact --before 30d --channel '#old-run' --dry-run
npx squawkbox compact --before 30d --channel '#old-run'Compaction writes matching rows to ~/.squawkbox/archive/transcript-*.jsonl
before rewriting transcript.jsonl.
What it does
- claim — announce a file or path you're working on, before you touch it.
- release — hand the path back when you're done.
- hand off — pass a task (and context) to another agent.
- ask — send a question to a specific agent or channel and block for the answer.
60-second quickstart
Zero to two agents talking in under a minute.
npx squawkboxWire each agent to the shim. Claude Code — .mcp.json in the project root:
{
"mcpServers": {
"squawkbox": {
"command": "npx",
"args": ["-y", "squawkbox", "shim", "--runtime", "claude"]
}
}
}Codex — ~/.codex/config.toml:
[mcp_servers.squawkbox]
command = "npx"
args = ["-y", "squawkbox", "shim", "--runtime", "codex"]That's it — the shim auto-spawns the daemon, so there's no server to manage. To run the daemon standalone, use npx squawkbox daemon. examples/demo.sh drives the whole loop with plain curl.
How it works
One daemon per machine owns the message store, presence, and long-poll engine. Each agent session spawns a tiny stdio MCP shim; the shim mints a stable handle (e.g. claude@web:main), registers, and relays five tool calls over a localhost HTTP API. Registration returns a per-session token, and the daemon derives from / reader identity from that session instead of trusting request JSON, so a raw caller cannot spoof another handle by changing from. The daemon binds 127.0.0.1 only and reads a random 32-byte bearer token from ~/.squawkbox/token (mode 0600). No cloud, no account, no network exposure. A single daemon is auto-spawned on first use. The recv tool is a blocking long-poll: the agent's turn suspends inside the call — burning zero tokens — until a matching message arrives or the timeout fires.
Claude shim squawkboxd Codex shim
(claude@web:main) (HTTP 127.0.0.1:7327) (codex@api:main)
| | |
| | recv(channel="#locks") | <- Codex turn
| |<-------------------------| parked here
| | [park: no msg yet] | (blocked)
| send("#locks","claim",| |
| "web/") | |
|---------------------->| append JSONL, emit |
| |------ match! ----------->| recv returns
| {ok, id} | | {from:claude@web:main,
|<----------------------| | type:claim, body:"web/"}
| | | Codex resumes, avoids web/The five tools
| Tool | What it does | Blocking? |
|---|---|---|
| send | Send a message to a handle, * (broadcast), or #channel; types: msg, claim, release, handoff, question, ack. | No |
| recv | Read new messages for your handle/channel; parks the turn until one arrives or the timeout fires. | Yes (long-poll) |
| who | List currently-connected handles and presence. | No |
| peek | Read recent messages without advancing your cursor. | No |
| rename | Set a new handle name (collisions auto-resolved). | No |
Reliability notes:
recv(from=...)uses filter-aware cursors, so filtered reads do not consume unrelated inbox/channel messages.- Cursors are snapshotted to
~/.squawkbox/cursors.jsonand survive daemon restarts. - The daemon rejects unknown message types, caps message bodies at 64 KiB, and caps
peek.limitat 100. - A
recvcursor means a message was returned, not that an agent finished processing it; useackmessages at the workflow level when processing acknowledgement matters.
Why "squawkbox"
A squawk box is an intercom — a talkback speaker for quick, direct chatter.
An aviation squawk code is how an aircraft broadcasts its identity so others can see it and not collide.
Which is literally the use case: agents broadcasting who they are and what they're touching, so they don't run into each other.
Status
v0, early, built in public. The locked v0 core is the daemon, the five-tool shim, auto-naming, auto-spawn, and the bearer token. A PyPI / uvx squawkbox convenience wrapper is planned for v0.1. Issues and PRs welcome.
License
MIT.
