claude-bus
A shared message and state bus that lets two or more Claude Code sessions
communicate, exchange state, and stay coordinated. It is a small MCP server
backed by a single SQLite file, plus an optional slash command and hooks.
The idea (and the one limit to understand first)
Claude Code works in turns: the model only "sees" outside data when a tool
returns a result or a hook injects text. There is no native push that wakes a
session mid-turn. So "staying connected" is built from two pieces:
- A shared store (the bus) every session reads and writes — for exchanging
state and messages.
- A poll convention or a hook that makes each session check the bus — so they
react to what the other left.
This project ships both.
Architecture
Session A (terminal 1) Session B (terminal 2)
claude -> bus (MCP) claude -> bus (MCP) one server per session
\ /
v v
~/.claude-bus/bus.db (SQLite WAL = the bus, the real shared state)
For the local stdio transport each session launches its own server process; they
share state through the SQLite file (WAL handles concurrent writes). For the HTTP
transport a single process serves every client. Both use the same schema, so they
are interchangeable backends.
Tools exposed: register, whoami, agents, send, inbox, message_status,
set_state, get_state, list_state, claim, release, list_claims.
What v0.2 changed (breaking)
- Per-recipient delivery. Each session has a read cursor; a broadcast reaches
every recipient independently (v0.1 lost it after the first reader).
inbox
returns {messages, pending_count}; inbox(peek=True) reads without consuming so
history can be re-read; late joiners still receive earlier broadcasts.
- Compare-and-set state.
set_state(key, value, expected_version=…) rejects a
stale write (the error reports the current version); mode="append" accumulates;
get_state now returns {value, updated_by, updated_at, version} and list_state
lists keys without values. message_status(id) shows who has read a message.
- Session-bound identity (stdio). The sender is the session's own registered
identity, not a free-text argument, so a session can't post as another. Call
register first; whoami reports your bound name. The HTTP transport serves many
clients from one process, so it takes identity explicitly and does not provide
this anti-spoofing guarantee.
- Advisory file claims.
claim(path, ttl=1800) / release(path) /
list_claims() announce "I'm editing this"; register(owns=[globs]) makes
agents() flag sessions whose files overlap. Claims are advisory — they never lock
the filesystem.
Existing ~/.claude-bus/bus.db files are migrated automatically (PRAGMA user_version): shared state is preserved; pending messages are reset (the read
model changed incompatibly).
Install — pick one
1. Quick local (one command)
No Python packaging, no plugin. Good for one machine.
bash install_claude_bus.sh # creates ~/claude-bus, registers the MCP server
It registers the bus server at user scope and installs the /bus command. Then
open two terminals, run claude in each, and type /bus backend / /bus frontend.
2. Claude Code plugin (recommended for sharing)
This whole repo is also a plugin and a single-plugin marketplace. Anyone with
Claude Code installs the MCP server, the /bus command, and the reactive hook in
one step. Requires uv on PATH (brew install uv),
which runs the bundled package with no manual venv.
/plugin marketplace add f3r21/claude-bus
/plugin install claude-bus@claude-bus
The plugin's .mcp.json runs the server via uvx --from "${CLAUDE_PLUGIN_ROOT}" claude-bus, so it works straight from the installed plugin directory.
3. PyPI + uvx (clean server install)
Publish the package, then any session points at it with zero venv management:
python -m build && twine upload dist/* # one-time publish
# then, on any machine:
claude mcp add -s user bus -- uvx claude-bus
uvx resolves and caches dependencies like npx for Python — this is the cleanest
fix if you hit venv issues (e.g. a pyenv build without the venv module).
4. HTTP server + Docker (multi-machine / always-on)
Run one shared bus other machines connect to by URL.
docker compose up -d # serves on :8765, data in a volume
# on each client machine:
claude mcp add --transport http bus http://YOUR_HOST:8765/mcp
Without Docker: pip install . && claude-bus-http (honors BUS_HOST, BUS_PORT,
BUS_DB).
Usage
Give each session an identity once, then talk normally: