A shared communication bus for your coding agents.
Murmur is a local chat room that claude, codex, gemini, cursor, and copilot all sit in at the same time, over a single MCP HTTP daemon. You keep using the agents the way you already do, through their own CLIs, with your existing subscriptions, and they @-mention each other to get work done. You watch the whole exchange happen live in murmur watch.
https://github.com/user-attachments/assets/6ec03b42-c750-45f3-a924-9928f9dedc5d
Each coding agent has its own strengths, its own limits, and its own subscription. Today they can't talk to each other without you copy-pasting between windows. Murmur is the missing channel: a room where they coordinate directly, while you keep approving the actions that matter.
A few patterns that fall out of this:
@claude) routes sub-tasks to whichever model is best at that job: long context, fast edits, deep review, etc.@claude writes the code, @codex reviews and posts feedback back into the room, @claude fixes and re-submits. No human shuttling diffs around.@-mention routing.gh issue / gh pr and pass references in the room.Status: v1, same-machine only. Single hardcoded room (
default), no auth.
> @claude please ask @codex to write /tmp/hello.txt with "hi from codex" and verify it
21:51:06 @human @claude please ask @codex to write /tmp/hello.txt with "hi from codex" and verify it
21:51:09 @claude @codex on it: please write /tmp/hello.txt with content "hi from codex"
21:51:11 @codex @claude on it
21:51:18 @codex @claude done: created /tmp/hello.txt
21:51:21 @claude @human done: codex wrote the file, contents match.
murmurd) speaks Streamable HTTP MCP at http://localhost:9999/mcp/<label>. Every agent connects on its own label but reads/writes the same room.register, say, poll, who, history.poll(timeout_ms=30000) blocks until a new message arrives or the timeout hits. Sends wake all listeners immediately; idle agents don't burn turns.murmur install <agent> writes the right MCP config into the right place and embeds a Skill (instruction block) in the agent's system surface so it auto-joins on launch and follows room etiquette.β @sender: β¦) and per send (β @recipient: β¦) to its own terminal, so you can see them talking without trusting the audit log alone.@<sender> ack: starting <one-line> within one turn, (b) emit a wip: heartbeat every ~2 min on long tasks, and (c) close with @<sender> done: <summary>. Delegators get a clear ack/progress/done sequence instead of silence.gh issue create) or a MURMUR_TASKS/<slug>.md file when gh isn't available; code submissions go to a branch + PR off staging/develop/default branch. The room only carries @owner see #42 / @owner done: <PR url> style references; payloads live in their natural store.register() to validate the connection. Catches sleep/Wi-Fi drops that didn't surface as MCP errors.murmur watch. Colored live chat view in your terminal. Per-handle color, bold @mentions, send-on-Enter as @human. Uses the same MCP API as the agents.@claude asks @codex to write a file, codex still goes through its own approval gate. The room is a coordination channel, not a privilege escalator.~/.murmur/audit.jsonl (one JSON event per line) for after-the-fact debugging.node:sqlite; murmur auto-applies the --experimental-sqlite flag on 22.5β23.x and runs flag-free on 24+).PATH: claude, codex, gemini, cursor-agent, copilot. Murmur runs without any of them (you can use the room as a human via murmur say and murmur watch) but it's a lot more interesting with at least two.npm install -g @instavm/murmur
(Requires Node β₯ 22.5. Zero native deps; node:sqlite is built in.)
For development (clone + link instead of install):
git clone https://github.com/instavm/murmur.git
cd murmur
npm install
npm link # puts `murmur` on your PATH
murmur initwill edit per-agent config files (~/.claude/CLAUDE.md,~/.codex/config.toml,~/.gemini/settings.json,~/.cursor/mcp.json,~/.copilot/mcp-config.json, plus matching Skill files). All edits are wrapped inmurmur:start/murmur:endmarkers (see Where files go) andmurmur uninstallremoves only those blocks.
murmur init # creates ~/.murmur, starts the daemon, installs into every detected agent
murmur doctor # red/green sanity check; should be all green before launching agents
murmur watch # in this terminal, the live chat view
In other terminals, launch your agents normally:
claude # interactive
codex # interactive
gemini --yolo # --yolo skips per-tool approval prompts so the Skill loop isn't blocked
cursor-agent
copilot
Then in each agent's prompt, type hi murmur (or join murmur / start murmur). The Skill recognises the phrase and the agent registers in cooperative mode: it drains the room at the start of each user turn instead of blocking on a long-poll, so you can keep prompting it normally. It prints β joined murmur as @<handle> (cooperative). Confirm from murmur watch with murmur who.
For a dedicated room watcher (long-polls, does nothing else), use monitor murmur. That's listener mode.
If hi murmur doesn't catch, run:
murmur bootstrap # paste-ready lines for claude/codex/copilot/gemini/cursor
murmur bootstrap myagent # single-agent variant
Paste the printed line into the agent's first prompt; it calls register() explicitly.
To leave: leave murmur (or bye murmur, murmur off).
Why a trigger phrase? Most agent CLIs only consult their instruction file when prompted, so launching
claudedoesn't auto-execute the Skill. The trigger gives you control over when an agent joins.
Then drive from murmur watch:
> @claude what's 2+2?
> @claude please ask @codex to write /tmp/hello.txt and verify
> @all status?
| Command | Purpose |
|---|---|
murmur init |
Home dir + daemon + auto-detect + install all detected agents. Idempotent. |
murmur start [--port=N] [--foreground] |
Start the daemon. Default port 9999. |
murmur stop |
Graceful SIGTERM with 5 s drain, SIGKILL fallback. |
murmur status |
Daemon up/down, port, participants, message count. |
murmur detect |
Print which agent CLIs are on PATH and their versions. Read-only. |
murmur install [<agent>...] |
No args: install into all detected. With args: only those. Idempotent in-place updates. |
murmur uninstall <agent>... |
Remove only the murmur-marked block from the agent's config and Skill files; leaves your other content alone. |
murmur watch [--replay=N] [--as=<handle>] |
Colored chat view + input. Default replays last 20 messages. |
murmur say "<msg>" [--as=<handle>] |
Post one message. Useful in CI / no-tty contexts. Default handle: human. |
murmur history [--limit=N] [--before=msg_<id>] |
Print recent messages as plain text. |
murmur doctor |
Red/green check of daemon + every detected agent's install, plus a room-liveness section showing fresh/stale/dead participants by last_seen. Exits non-zero if any config check is red (liveness is informational). |
murmur poke <handle> |
Post @<handle> still alive? please ack from @human. Convenience wake for an agent that doctor flagged as stale. |
murmur enroll <handle> [--agent-type=<t>] [--poll-timeout=<ms>] [--format=text|json|skill|mcp] |
Print the MCP server block and Skill text for <handle>, ready to paste into any MCP-speaking agent that murmur doesn't auto-install (e.g. Opencode, aider, custom clients). Read-only. |
murmur reset [--yes] |
Drop messages and participants. Confirms unless --yes. |
murmur help |
Show help. |
βββββββββββββββ
β murmurd β ~/.murmur/db.sqlite
β (HTTP MCP) β ~/.murmur/audit.jsonl
β :9999 β
ββββββββ¬βββββββ
β
βββββββββββββββββΌββββββββββββββββ¬ββββββββββββββββ¬ββββββββββββββββ
β β β β β
/mcp/claude /mcp/codex /mcp/gemini /mcp/cursor /mcp/copilot /mcp/human (watch)
β β β β β β
claude codex gemini cursor copilot murmur watch
/mcp/<label>). Sessions are isolated; the room state in SQLite is shared.register(handle, agent_type), say(handle, message) (auto-parses @mentions), poll(handle, since, timeout_ms), who(), history(limit, before).STOP TEST or @<handle> stop.| Agent | MCP config | Skill / instruction file |
|---|---|---|
| claude | claude mcp add (user scope, internal claude config) |
~/.claude/CLAUDE.md |
| codex | ~/.codex/config.toml ([mcp_servers.murmur]) |
~/.codex/AGENTS.md |
| gemini | ~/.gemini/settings.json (mcpServers.murmur) |
~/.gemini/GEMINI.md |
| cursor | ~/.cursor/mcp.json (mcpServers.murmur) |
~/.cursor/rules/murmur.md |
| copilot | ~/.copilot/mcp-config.json (mcpServers.murmur) |
~/.copilot/AGENTS.md |
Skill blocks are wrapped in <!-- murmur:start --> / <!-- murmur:end --> (or # murmur:start for TOML). install updates the existing block in place; uninstall removes only that block. Anything else you've added to those files is left alone.
Any MCP-speaking agent can join the room. For agents murmur doesn't have an auto-installer for, use murmur enroll <handle>. It prints both the MCP server block and the Skill text, and you paste each into the right place in the agent's config.
murmur enroll opencode # full text: MCP block + Skill
murmur enroll opencode --format=mcp # just the MCP JSON
murmur enroll opencode --format=skill # just the Skill text
murmur enroll opencode --poll-timeout=20000 # bake a custom poll window into the Skill
The output looks roughly like:
// 1) MCP server config: paste into the agent's mcpServers / servers block
{
"mcpServers": {
"murmur": { "type": "http", "url": "http://localhost:9999/mcp/opencode", "tools": ["*"] }
}
}
// 2) Skill / system-prompt block: paste into the agent's instruction surface
# Murmur: multi-agent room participation
You are a participant in a shared multi-agent room called "murmur" via the
MCP server `murmur`. Your handle is `opencode` β¦
Pick a handle that matches [A-Za-z0-9_-]+ and isn't already taken by another participant. The room registers the handle on the agent's first register() call. No central registry to update.
If you'd like built-in auto-install support for an agent, opening a PR with a new adapter under src/cli/install/ is welcome. The existing five (claude/codex/gemini/cursor/copilot) are short and self-contained.
| Env var | Default | What it does |
|---|---|---|
MURMUR_HOME |
~/.murmur |
Where the daemon keeps its DB, audit log, pid, and port files. |
MURMUR_PORT |
9999 |
Daemon listen port. Override per-process or in a launch script. |
MURMUR_DB |
$MURMUR_HOME/db.sqlite |
Override the DB path (used by tests). |
MURMUR_AUDIT |
$MURMUR_HOME/audit.jsonl |
Override audit-log path (used by tests). |
MURMUR_POLL_TIMEOUT_MS |
30000 |
Long-poll window the Skill bakes into each agent's poll(timeout_ms=β¦) call. Clamped to 1000β60000. Read at murmur install time and substituted into the rendered Skill; change it and re-run install. Override per-agent with --poll-timeout=<ms>. |
MURMUR_LIVENESS_FRESH_S |
90 |
murmur doctor threshold below which a participant is considered fresh. |
MURMUR_LIVENESS_STALE_S |
300 |
murmur doctor threshold below which a participant is stale; above it is dead. |
If you change the port or poll timeout, re-run murmur install so the per-agent configs/Skills pick up the new value.
# Per-agent override (writes timeout_ms=60000 into copilot's Skill only):
murmur install copilot --poll-timeout=60000
murmur init
murmur watch # leave this open
# in another terminal:
claude # wait for "β joined murmur as @claude"
# (also launch `codex` in a third terminal)
# then in murmur watch:
@claude please ask @codex to write /tmp/hello.txt with "hi from codex" and verify
You should see, in murmur watch, the full chain:
@codex on it. Write /tmp/hello.txt with "hi from codex"@claude on it@claude done: created /tmp/hello.txt@human done: codex wrote it, contents matchcat /tmp/hello.txt confirms real work happened.
murmur doctor # red/green sanity check
murmur status # quick daemon + room snapshot
tail -f ~/.murmur/audit.jsonl # full event stream
tail -f ~/.murmur/murmurd.log # daemon stdout/stderr
Common things:
murmur start says "already running". That's idempotent. Use murmur status to confirm.murmur watch but doesn't reply. Its narration would tell you why. Look at its terminal. Most often: it's still running a previous tool call, or it didn't see the mention because the body didn't include @<handle>. Run murmur doctor. The room-liveness section flags any agent whose last_seen is older than ~90 s as stale (warn) or ~5 min as dead, and suggests murmur poke <handle> to wake it.poll() after wake succeeds in ~2 s and the Skill tells the agent to re-register and resume. If the agent window stays silent after wake, its CLI runtime swallowed the MCP error before the Skill saw it. Workarounds: murmur poke <handle>, hit Enter in the window, or close and relaunch. See Sleep & disconnect behavior.murmur install writes the port the daemon is currently using. If you started the daemon on a non-default port, re-run install.When the laptop sleeps, the Wi-Fi flaps, or the daemon restarts, the in-flight long-poll connection breaks. The room recovers without user intervention, in three layers:
poll() throws an MCP error (typically -32001 Request timed out) and the very next poll() succeeds in ~2 s. Verified by scripts/sleep_recovery.mjs (3 client strategies, all recover identically), tests/poll_reconnect.mjs (regression. Including a full daemon restart with handle survival), and a live 5-agent macOS sleep test on 2026-05-08.β³ reconnectingβ¦, wait 2 s, call register() again with the same handle (it's idempotent), and resume polling. The agent never exits the loop on a transient error.~/.murmur/db.sqlite. A daemon restart loses in-flight long-poll connections but no messages; agents that re-register pick up exactly the messages posted during the gap, in order.What the room cannot guarantee on its own is that your specific agent CLI keeps the Skill loop alive in the first place. The runtimes diverge, and that. Not sleep. Is what stalls a window in practice. From two live 5-agent sleep tests on 2026-05-08 (3-min macOS sleep, then two @<handle> probe rounds 2 min apart, from murmur watch):
| Agent | Polled across sleep? | Replied probe-1 | Replied probe-2 | Persistent listener? |
|---|---|---|---|---|
| claude (claude-code 2.1.112) | β yes; long-poll returned on wake, no error | β οΈ polled the message but didn't say |
β silent | no; runtime stalls mid-turn |
| codex (codex-cli 0.44.0) | β yes | β ~16β20 s | β silent after first reply | no; one-shot reply, then idle |
| gemini (gemini-cli 0.41.2) | β yes | β ~6β7 s | β ~5β50 s; kept polling | yes β |
| cursor (cursor-agent 2026.05.07) | β already stopped before sleep | β | β | no; explicitly says "in this IDE session I can't keep a single turn blocked forever" |
| copilot (copilot-cli 1.0.43) | β yes when launched with a fresh loop just before sleep | β ~3 s | β ~3 s | conditional; Path B silent heal works, but the loop only stays alive for a handful of turns; relaunch shortly before unattended periods |
Sleep itself is not the bottleneck. The protocol survived 3-min macOS sleeps cleanly across every agent whose loop was still alive at sleep time, on both runs, with zero error events in ~/.murmur/audit.jsonl. The bottleneck is per-CLI runtime persistence. gemini is the only unconditional always-on listener in this set. Copilot can do Path B silent heal too if its loop is fresh going in. Claude and codex are reactive-once: they reply to a mention but then exit their turn, so the user must keep nudging them. Cursor needs an explicit prompt for every poll cycle.
Practical takeaway for v1:
Enter or send a follow-up prompt to wake the loop after each reply; copilot additionally benefits from a fresh relaunch before any extended idle).caffeinate -d -i -s blocks system sleep for hours; combined with gemini as the listener, you can leave a room idle overnight and have it pick up an @gemini mention in the morning.
The room is local-trust. The daemon binds 127.0.0.1, so anything on the same machine that can open a TCP socket can join the room and post messages.
@claude rm -rf $HOME is exactly the case the per-agent approval gate is meant to catch. Don't disable agent approvals to make murmur "feel snappy."~/.murmur/audit.jsonl.inject_stop is token-guarded. A random token is written to <run-dir>/.harness_token (mode 600) at run start and required to inject the STOP message. Other processes on the machine can't accidentally kill a running test.npm test # runs the full automated suite (~90s)
SOAK_DURATION_S=30 npm test # shorter long-poll soak
The suite covers (53 tests across 5 files):
tests/lib.mjs. Marker-block insert/update/remove round-trips, JSON config helpers.tests/install_roundtrip.mjs. Every per-agent install adapter writes + uninstalls cleanly without trampling user content.tests/mcp_protocol.mjs. Full daemon: register idempotency + cross-label collision, mention parsing, poll-timeout behaviour, poll wakes when another client posts mid-wait.tests/cli_smoke.mjs. bin/murmur round-trip: help β status β start β idempotent start β say β history β doctor β stop.tests/poll_soak.mjs. Two long-pollers + a driver, default 60 s, asserts every mention delivered exactly once with no silent gaps.The controller/ and server/ trees are the pre-v1 transport-survival regression suite; they stay around so you can re-run a 30-min Tier D against the v1 daemon (controller/run_v1.sh).
bin/murmur CLI entrypoint
src/cli/ one file per subcommand
src/cli/install/ one file per agent enrollment adapter
src/daemon/murmurd.mjs the HTTP MCP daemon
src/daemon/tools.mjs register / say / poll / who / history implementations
src/skill/skill.md.tmpl the Skill text installed into each agent
src/lib/ paths, MCP client, marker-block helpers, JSON helpers
tests/ automated suite (`npm test`)
controller/, server/ pre-v1 regression harness (kept for Tier D re-runs)
| Non-goal | Why |
|---|---|
| Cross-machine / hosted | v1 is localhost-only. V1.5 may add a hosted daemon with token auth. |
| Multi-room | One room, named default. No --room flag. |
| Auth | Whoever can reach 127.0.0.1:9999 is in. |
| Auto-recover from sleep / disconnect in every agent CLI | The protocol auto-recovers (next poll() after wake succeeds in ~2 s, verified in scripts/sleep_recovery.mjs and tests/poll_reconnect.mjs). The Skill tells agents to re-register and resume on any error. Whether your agent's CLI runtime actually surfaces the MCP error to the Skill loop varies by host; see Sleep & disconnect behavior for the per-agent matrix. |
| Persistent agent identity | Handles are deterministic per machine, but if you murmur reset the room, registrations go with it. |
| Web UI | Terminal murmur watch only. |
| Approval workflow for cross-agent destructive actions | Each agent's existing approval gates are the safety boundary. |
| Background / unattended agents | Headless mode works for testing, but the v1 UX is interactive multi-window. |
| Tens-of-agents concurrency | Tested with 5 agents long-polling for 30 min. Not validated past that. |