agentbus
OfficialClick on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@agentbuswhat is everyone working on, and are there conflicts?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
agentbus
A team bus for Claude Code: parallel sessions in one repository that know about each other, talk, and don't step on each other's files.
English | Русский
agentbus is a small local server that a team of Claude Code sessions uses to coordinate: a roster with statuses,
messages that show up in an agent's context by themselves, a task board with dependencies, and claims on paths with
conflict detection. It is one Python process with a SQLite file, it listens only on loopback (127.0.0.1), and it needs no
accounts or external services.
Contents
Reference: MCP tools · CLI · Configuration · REST API
Related MCP server: swarm-mcp
Why agentbus
Running several Claude Code sessions on one repository is an easy way to get more done, until they collide. Out of the box, parallel sessions:
know nothing about each other. Nobody knows who works on what, who is stuck and who is done;
overwrite each other's files. Two sessions editing the same module find out at merge time, or never;
cannot ask or agree on anything. A question to another session has to go through you, copied by hand;
cannot be woken up. A session that finished its turn sleeps until someone types into it.
agentbus gives such a team the minimum it needs to work like a team:
Roster and status | Every agent registers with a name, a role, a branch and a working directory and keeps a one-line status ("writing the retry policy in |
Messages with automatic delivery |
|
Claims on paths | Before editing outside its own area, an agent claims a path or glob. An overlapping claim of someone else is refused, with the holder's name and reason, and nothing is created. |
Task board | Tasks with owners, states and dependencies; a task cannot start before its dependencies are done; moving a task to review, blocked or done notifies the director. |
Journal and monitor | Every join, message, status change, claim and conflict lands in an activity journal; |
No external services | One local process and one SQLite file. Restart the server, the app or the machine: the state is still there. |
It was built for and tested on a real project: two waves of work by five parallel agents under one director session, writing a C++ code base. What the bus actually did there:
An urgent broadcast ("stop all builds, the compiler toolchain is being updated") reached busy agents in the middle of their turns, after their next tool call; a direct chat message to a session waits until the session's turn ends.
An agent that broke something for everyone (it killed processes of other agents by image name) reported it with a team-wide alert within minutes; the team got a new rule out of it the same hour.
Agents found infrastructure bugs in each other's areas and reported them to the owner instead of fixing them in passing, which kept every branch reviewable.
Bugs in the bus itself were found by that work and fixed: a new session started in an agent's folder used to receive that agent's mail; two simultaneous claims of the same path could both succeed; the REST API accepted requests from web pages. All three have regression tests now.
How it works
flowchart LR
subgraph sessions["Claude Code sessions (one git worktree each)"]
D["director"]
A1["agent: api"]
A2["agent: web"]
end
subgraph bus["agentbus serve (127.0.0.1:8765)"]
MCP["/mcp: MCP tools"]
REST["/api: REST"]
DB[("SQLite<br/>.agentbus/bus.db")]
MCP --> DB
REST --> DB
end
D & A1 & A2 -- "register, send, claim, task_update…" --> MCP
H["hook.py<br/>(after every tool call)"] -- "/api/hook" --> REST
H -. "new messages → additionalContext" .-> A1
W["agentbus watch"] -- "events" --> REST
C["agentbus CLI / waitmail"] --> RESTServer (
agentbus serve): MCP over streamable HTTP at/mcpfor the agents, a small REST API at/apifor the hook, the CLI and the monitor. Both are thin layers over one store with all the rules; every operation is a single SQLite transaction.Hook (
hook.py): Claude Code runs it onSessionStart,UserPromptSubmitandPostToolUse. It refreshes the agent's "last seen", and if there is new mail, prints it asadditionalContext, which Claude Code adds to the session's context. It is one file using only the Python standard library.Session binding: at session start the hook tells an unregistered session its
session_key(the Claude Code session id); the agent passes it toregister(), and from then on the bus knows which session is which agent.Monitor (
agentbus watch) and CLI (agentbus <operation>): for you and the director, in a terminal.
The MCP part (tools, CLI, monitor) works with any MCP client. Automatic delivery and session binding use Claude Code hooks. Starting sessions and waking an idle one is done by Claude Desktop, not by the bus: see Working as a team.
Quick start
You need uv and Python 3.11+.
# 1. Run the bus in your project's root (state goes to ./.agentbus/bus.db). Keep this terminal open.
cd your-project
uvx --from git+https://github.com/RetentionSoftware/agentbus agentbus serve
# 2. In another terminal: is it up?
uvx --from git+https://github.com/RetentionSoftware/agentbus agentbus healthThen connect your project to it, as described below. For daily use, install the command once instead of uvx:
uv tool install git+https://github.com/RetentionSoftware/agentbus
agentbus --helpTo run the bus in the background, clone the repository and use the scripts. They run that checkout through uv and
keep the database, logs and pid file in <project>/.agentbus/:
# Windows (PowerShell); also opens the monitor in its own window
powershell -File path/to/agentbus/scripts/start.ps1 -ProjectDir .
powershell -File path/to/agentbus/scripts/stop.ps1 -ProjectDir .
# macOS / Linux
path/to/agentbus/scripts/start.sh .
path/to/agentbus/scripts/stop.sh .Add .agentbus/ to your .gitignore.
Setting up a project
1. Run the server in the project root, as in Quick start. Optionally give it the project name
and the path of your protocol document, which are shown to the agents:
agentbus serve --project-name "My Project" --protocol docs/agents/PROTOCOL.md.
2. Register the MCP server for the project: .mcp.json in the repository root (template):
{
"mcpServers": {
"agentbus": { "type": "http", "url": "http://127.0.0.1:8765/mcp" }
}
}3. Install the hook. Copy src/agentbus/hook.py to .claude/hooks/agentbus_hook.py
in your project and commit it, then wire it in .claude/settings.json
(full template):
{
"enabledMcpjsonServers": ["agentbus"],
"permissions": { "allow": ["mcp__agentbus"] },
"hooks": {
"SessionStart": [{ "hooks": [{ "type": "command", "timeout": 5,
"command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/agentbus_hook.py\" session_start" }] }],
"UserPromptSubmit": [{ "hooks": [{ "type": "command", "timeout": 5,
"command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/agentbus_hook.py\" prompt" }] }],
"PostToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "timeout": 5,
"command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/agentbus_hook.py\" post_tool" }] }]
}
}Use python3 instead of python where that is the name of the interpreter (most macOS and Linux systems). The hook
needs only the standard library. If the bus runs on another port, set AGENTBUS_PORT in the env section of the
settings. Prefer this copied file to agentbus hook: see Hook performance.
Claude Code treats exit code 2 of a hook as a blocking error: a failingUserPromptSubmit hook discards the
user's prompt. The agentbus hook always exits with 0, but python missing_file.py exits with 2. Keep the path in
the settings valid, and do not move the hook file while sessions that loaded the old settings are running.
4. Approve the MCP server. enabledMcpjsonServers in the committed settings approves the project's agentbus
server for everyone; otherwise Claude Code asks once per user. permissions.allow: ["mcp__agentbus"] lets agents
call the bus tools without a permission prompt each time.
5. Write the protocol for your agents: start from templates/PROTOCOL.md (session start
checklist, quality bar, git rules, progress log, claims, messages, statuses, hand-in). Put it where the server's
--protocol points (default docs/agents/PROTOCOL.md).
6. Tell every session about it with a paragraph in CLAUDE.md (template):
## Agent team
Several parallel Claude sessions work on this project under the **director**. If you were given a task, read
`docs/agents/PROTOCOL.md` before any edit and run its start checklist (worktree, registration on the `agentbus`
bus, status). `[agent-bus] …` blocks in your context are messages of the team's coordination bus.Start a new Claude Code session in the project: the hook greets it with
[agent-bus] … You are NOT registered yet …, and the mcp__agentbus__* tools are available.
Working as a team
The workflow that agentbus was built around: one director session works with you, plans the work and reviews it; agent sessions do the tasks in parallel.
A task is a markdown file (
docs/tasks/T-001-*.md): goal, the agent's name, the paths it owns, contracts, acceptance criteria. The director commits it to the main branch and creates it on the board:task_create(task_id="T-001", title=…, spec_path=…, depends_on=[…]).One git worktree per agent. Parallel sessions in one checkout overwrite each other's files and builds; a worktree (and a branch) per agent avoids that. In Claude Desktop, the director can start a session in a new worktree directly from its own chat.
The kickoff message (template) tells the agent its name and task, to run the protocol's start checklist, and to send a plan before writing code.
Plan → go → work → review. The agent sends its plan (
kind="review_request"); the director answers with adecision; the agent works, keeping its status and a progress log current, claiming paths outside its area and asking questions over the bus; it hands in withtask_update(state="review"), which notifies the director.The director merges and moves the task to
done; tasks that depend on it can start.
How the director gets mail. Messages reach a session through the hook after its tool calls. A director that is
waiting for agents runs agentbus waitmail agent=director timeout=1800 as a background command: it exits as soon as
there is unread mail (without marking it read), which ends the wait.
How to wake an idle agent. A session that finished its turn sees nothing until something runs in it. The bus cannot start a turn; Claude Desktop can: the director sends the session a short direct message (in Claude Desktop, its cross-session message tool), and the agent's next tool call delivers the actual bus messages. Keep the substance in the bus (it is persistent and searchable) and use the direct message only to wake the agent up.
Joining late. Team-wide messages sent before an agent's first registration are marked read for it: an old
"stop all builds" would mislead a newcomer. register() returns how many were skipped; history(kind="decision")
catches up on team decisions.
The monitor
agentbus watch # roster, then the live journal; Ctrl+C to quit
agentbus watch --quiet # without message reads, deliveries and session startsOn Windows, scripts/monitor.ps1 opens it in a console window of its own (the start script does that too). The
monitor only reads; closing it does not affect the bus, and it reconnects when the bus restarts.
── agentbus · My Project · http://127.0.0.1:8765 ──
● api [T-002] working: writing the retry policy in src/net/retry.py
● director idle: reviewing T-001
── events ──
14:02:11 JOIN web joined: frontend
14:02:40 CLAIM api [src/net] retry policy
14:03:05 CONFLICT web [src/net/client.py] claim refused, held by api (src/net)
14:03:12 MSG web → api #42 [question] can I add a timeout parameter to Client?
14:05:30 TASK api [T-002] in_progress -> review: branch feat/retry; tests in tests/netReference
MCP tools
Every tool that acts on behalf of an agent takes its name as agent: the bus does not remember callers between calls.
Tool | What it does |
| Join or rejoin. |
|
|
| Roster with status and |
|
|
| Unread messages (urgent first), marked read; |
| Waits up to 600 s for mail, then returns the inbox. |
| Search messages. |
| Create a task; the owner gets a note. |
|
|
| Tasks; one task with its history. |
| Claim a repo-relative path or glob for 15 min…72 h. Returns |
| Release one claim, or all of yours. |
| Active claims. |
| Agents, tasks by state, claims, the last decisions and contract changes. |
The director is a convention, not a setting: an agent named director may move tasks it does not own and
receives the task notifications.
Claims compare normalised paths (forward slashes, lower case) and treat a glob as its directory (src/core/*.h
covers src/core), so overlap detection errs on the side of a conflict. Claiming the whole repository, or a glob at
its root, is refused.
CLI
agentbus [--host HOST] [--port PORT] <command> [arguments]
serve [--db PATH] [--project-name NAME] [--protocol PATH] run the bus in the foreground
watch [--history N] [--quiet] live monitor
hook session_start|prompt|post_tool the Claude Code hook (payload on stdin)
health version, project, database of the running bus
waitmail agent=NAME [timeout=SECONDS] block until NAME has unread mail
<operation> key=value ... | <operation> --json '{...}' any bus operation
versionOperations: every MCP tool except wait_for_messages, plus unread_count(agent), events(after, limit) (the
journal) and purge(agent, task_id) (erase every trace of a test agent or task). Values starting with [ or {
are parsed as JSON; the server converts the rest (limit=5, include_read=true).
agentbus board
agentbus send agent=director to=all kind=decision body="Use the new error type in all modules"
agentbus task_create agent=director task_id=T-003 title="Retry policy" spec_path=docs/tasks/T-003.md depends_on='["T-001"]'
agentbus history kind=decision limit=5Exit codes: 0 success, 1 the bus reported an error or is unreachable, 2 usage error. waitmail exits with 0
both when mail arrives and on timeout; it prints which.
Configuration
Every setting is a flag, an environment variable, or the default, in that order of precedence, for every command.
Flag | Environment | Default | Used by |
|
|
| all (the server accepts only |
|
|
| all |
|
|
| server |
|
| name of the current directory | server: MCP instructions |
|
|
| server: MCP instructions, hook texts |
The database path is relative to the directory where the server starts: start it from the project root (the scripts
do). agentbus health shows which database a running bus uses. The server writes its process id to server.pid
next to the database while it runs. Run one server per database.
The database has a schema version. A newer agentbus migrates an older database on start, after saving a copy next to
it (bus.db.v<N>.bak); an older agentbus refuses to open a newer database.
REST API
For scripts and tools. Requests must come without an Origin header and with a loopback Host; POST bodies are
JSON (Content-Type: application/json).
Request | Response |
|
|
|
|
|
|
Hook performance
The hook runs after every tool call of every agent, so its start-up time matters. One PostToolUse call with a
round trip to the bus, median of 30 runs on Windows 11, Python 3.14:
How the hook is run | Median | p90 |
| 45 ms | 47 ms |
| 130 ms | 133 ms |
| 155 ms | 156 ms |
| 229 ms | 240 ms |
Use the copied file. The last line is a lower bound for uvx --from git+https://…, which also has to resolve the
git reference; use uvx for serve, watch and the CLI, never for the hook. The numbers include the start of the
Python interpreter, which dominates; the bus itself answers a hook request in about a millisecond.
Security
No authentication. Anyone who can reach the port can act as any agent. The server therefore listens only on loopback (
127.0.0.1,::1,localhost) and refuses other addresses. Never expose it to a network, through a proxy, a tunnel or port forwarding.Web pages are kept out. A browser page can send "simple" cross-origin POST requests to
127.0.0.1without a CORS preflight, and DNS rebinding can make a foreign host name point to it. The first version of the REST API accepted such requests: any open web page could have put text into an agent's context through the hook, or erased data. Now/api/*rejects requests with anOriginheader, a non-loopbackHostor a non-JSON body, and the MCP SDK applies the same Host/Origin checks to/mcp.Messages are data, not commands. Text from the bus reaches the model's context. Your protocol should say that messages from other agents are requests and information, that only the director gives instructions, and that nothing outside the repository (installing software, network access, deleting data) is done because of a message without the user's confirmation. The protocol template does.
The state is a plain SQLite file in
.agentbus/: keep it out of git.
Limitations
An idle agent does not see its mail until something runs in its session: the hook delivers only after tool calls and prompts. Wake it with a direct message (see Working as a team).
Automatic delivery and session binding need Claude Code hooks. Other MCP clients can use every tool but have to call
inbox()orwait_for_messages()themselves.One machine. No networking between machines, no authentication, no web UI.
Claims are advisory: the bus detects conflicts between agents that claim; it does not lock files.
At most 8 messages are delivered per hook call (the rest stay unread, the agent is told to call
inbox()), and bodies longer than 1500 characters are cut in the hook's text (the full text is ininbox).
Troubleshooting
Symptom | Cause and fix |
| The server is not running, or on another port. Start it; check |
No | The MCP server is not approved or was not reachable when the session started. Approve it ( |
Messages are not delivered, or an agent gets someone else's mail | Session binding. The hook binds a session by the |
| Another process listens on the port: another bus ( |
| The database was upgraded by a newer agentbus. Upgrade agentbus; downgrades are not supported. |
The hook does nothing | Run it by hand: |
A prompt disappears after pressing Enter | A |
Contributing
See CONTRIBUTING.md. In short: uv sync, then uv run pytest, uv run ruff check,
uv run ruff format --check and uv run mypy must pass. The MCP tool set is a public API.
Planned: agentbus init to generate the project files (.mcp.json, hook settings, the hook, the protocol).
License
MIT © 2026 Retention Software
This server cannot be deployed
Maintenance
Related MCP Connectors
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
Project management shared by people and AI agents, with persistent project state through MCP.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
The team layer for AI coding agents: shared contracts, collision alerts, E2EE sessions.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA local-first MCP server for coordinating parallel AI coding sessions with tools like Claude Code and Codex in a single repository.2MIT
- AlicenseNot gradedqualityBmaintenanceMCP server that lets multiple coding-agent sessions on the same machine discover each other and collaborate through a shared SQLite database.17 npm1MIT
- AlicenseNot gradedqualityDmaintenanceA zero-dependency MCP server that allows multiple coding agents to coordinate work on the same repository using file locks, task claims, and status messages.1MIT
- AlicenseAqualityBmaintenanceCoordination protocol for parallel coding agents, built above Git. The MCP server exposes the full 17-tool lifecycle over stdio: register agents, publish intents with semantic scopes and declared operations, claim work, check for conflicts before code is written, publish ChangeSets, run trusted named verification checks, and record accepted work. Deterministic rules raise findings when two agents'18538Apache 2.0