Created: March 25, 2026
Last commit: May 16, 2026
TypeScript57.8%
Rust27.6%
Python14.1%
JavaScript0.5%
distributed-systemscross-agent-discoverytask-handoffssqlitefile-lockingdag-based-workflowsTTL-cleanupheartbeatmulti-sessionDistributed systemsCross-agent discoveryAgent coordinationShared SQLite databaseFile lockingTask handoffsDAG-based workflowsBunbun runbun:sqliteWrite-Ahead LoggingWALSWARM_DB_PATHMCPMCP serverregisterderegisterlist_instancesremove_instancewhoamisend_messagebroadcastpoll_messageswait_for_activityrequest_taskrequest_task_batchclaim_task
README.md

swarm-mcp

MCP server that lets multiple coding-agent sessions on the same machine discover each other and collaborate through a shared SQLite database.

Each session spawns its own swarm-mcp server process via stdio. They all share one SQLite file at ~/.swarm-mcp/swarm.db by default. No daemon needed.

GitHub


Quick start

New here? Read docs/quickstart.md first. It walks you from zero to two Claude Code sessions seeing each other in about five minutes, with the expected output at each step.

The rest of this section is a condensed reference for non-Claude hosts. For a first-run walkthrough on a local clone, see docs/getting-started.md. For the broader modular architecture this repo is growing toward, read docs/control-plane.md. Backend and consumer config lives in docs/backend-configuration.md.

Install dependencies:

cd /path/to/swarm-mcp
bun install

Add the server to your coding agent using that host's MCP config format. Bun is the simplest dev/runtime path because the examples use bun run, but the built dist/*.js entrypoints also run under Node 20+ with better-sqlite3.

Codex (~/.codex/config.toml)

[mcp_servers.swarm]
command = "bun"
args = ["run", "/path/to/swarm-mcp/src/index.ts"]
cwd = "/path/to/swarm-mcp"

opencode (~/.config/opencode/opencode.json)

{
  "mcp": {
    "swarm": {
      "type": "local",
      "command": ["bun", "run", "/path/to/swarm-mcp/src/index.ts"],
      "enabled": true
    }
  }
}

Claude Code (~/.claude.json)

{
  "mcpServers": {
    "swarm": {
      "command": "bun",
      "args": ["run", "/path/to/swarm-mcp/src/index.ts"]
    }
  }
}

Tool names are usually namespaced by the client using the server name. Depending on the host you may see swarm_register, mcp__swarm__register, or other variants. Use whichever form your host exposes.

Call the swarm register tool first to join the swarm.

Install the packaged skill

Mounting the MCP server makes the swarm tools available, but agents still benefit from the bundled SKILL.md workflow. If your host supports installable skills (Claude Code, OpenCode, Codex with skills, etc.), install skills/swarm-mcp for coordination. Symlink is recommended over copying so updates from git pull propagate automatically:

# In your consumer project root
mkdir -p .agents/skills .claude/skills
ln -s /absolute/path/to/swarm-mcp/skills/swarm-mcp .agents/skills/swarm-mcp
ln -s ../../.agents/skills/swarm-mcp .claude/skills/swarm-mcp

Or install globally for all projects:

mkdir -p ~/.claude/skills
ln -s /absolute/path/to/swarm-mcp/skills/swarm-mcp ~/.claude/skills/swarm-mcp

Then invoke /swarm-mcp planner, /swarm-mcp implementer, etc., when starting role-specialized sessions. Full per-host install paths and copy-based alternatives live in docs/install-skill.md.

Further reading


MCP server vs swarm-server

The TypeScript swarm-mcp process is the stdio MCP server used by coding-agent hosts. It is enough for local multi-agent coordination through tools, resources, prompts, and the shared SQLite database. Its core job is the coordination bus: instance identity, tasks, messages, locks, KV, and best-effort wakeups.

Spawner backends are adapters around that bus. The default adapter is herdr; swarm-ui remains available as a fallback/control-surface adapter. New terminal managers should plug in as spawner/workspace backends rather than changing the task/message/lock contract.

The Rust apps/swarm-server daemon is a separate desktop/mobile control plane. It serves swarm-ui over a local Unix socket, exposes HTTPS/WSS on port 5444 for paired clients, manages PTYs, and reads the same swarm.db. It is not required for the basic MCP setup above. The current apps/swarm-ios workstream is Herdr-bridge first so Herdr remains the universal PTY owner; swarm-server remains useful reference material and the daemon for swarm-ui. See docs/swarm-server.md.

Control-plane overview

Control-plane and backend configuration overview

Source: docs/diagrams/backend-configuration.mmd. Backend selection and workspace identity conventions are centralized in docs/backend-configuration.md.


How it works

All sessions read and write to ~/.swarm-mcp/swarm.db by default using WAL mode, auto-vacuum, and a 3s busy timeout. Bun uses bun:sqlite; Node uses better-sqlite3.

Set SWARM_DB_PATH before launching the server if you want a different database location. Work/personal identity-separated setups should use separate paths, for example ~/.swarm-mcp-work/swarm.db and ~/.swarm-mcp-personal/swarm.db; see docs/identity-boundaries.md.

When you call register, the server starts a 10s heartbeat and a 5s notification poller.

Registration fields

The register tool accepts these parameters. Only directory is required.

FieldRequiredDescription
directoryYesThe live working directory for the current session.
scopeNoShared swarm boundary. Sessions in the same scope can see each other; different scopes are different swarms. Defaults to the detected git root, or to directory when no git root exists. Use a new scope only for a separate swarm; do not split frontend/backend inside one repo with scope. Use team: label tokens for that.
file_rootNoCanonical base path for resolving relative file paths in lock_file and task files. Useful when disposable worktrees should share one logical file tree.
labelNoFree-form identity text. Recommended convention: machine-readable space-separated tokens like identity:work provider:codex-cli role:planner. The identity: token should match the launcher/config root when using identity separation. The role: token is optional; if missing, the session is treated as a generalist.

Task features

Tasks support several features for building autonomous DAG-based workflows:

FeatureDescription
priorityInteger (default 0). Higher = more urgent. list_tasks returns tasks sorted by priority descending. Implementers can use claim_next_task to atomically claim the highest-priority compatible task.
depends_onArray of task IDs. A task with unmet dependencies starts as blocked and auto-transitions to open when all deps reach done. If any dependency fails, downstream tasks are auto-cancelled.
idempotency_keyUnique string. If a task with this key already exists, request_task returns the existing task instead of creating a duplicate. Essential for crash-safe plan retries.
parent_task_idOptional parent task ID for tree-structured work tracking.
review_of_task_idOptional task ID that a review task is reviewing. Supports $N references inside request_task_batch.
fixes_task_idOptional task ID that a fix task addresses. Supports $N references inside request_task_batch.
progress_summary / progress_updated_atFirst-class progress fields maintained by report_progress so peers can inspect long-running work without interrupting.
blocked_reason / expected_next_update_atOptional progress metadata for work that is blocked or needs a follow-up heartbeat by a specific Unix timestamp.
approval_requiredIf true, task starts in approval_required status and must be approved via approve_task before work begins. Use this for true approval gates, not routine code review.

Task statuses: open, claimed, in_progress, done, failed, cancelled, blocked, approval_required.

Session resets and prompt compaction

If a host compacts context, starts a fresh window, or loses the previous bootstrap, rejoin the swarm the same way:

  1. Call register again.
  2. Rehydrate with bootstrap.
  3. For planners, also check kv_get("owner/planner") and kv_get("plan/latest").

The durable coordination state lives in the shared database, not in repeated per-tool prompt text.


Auto-cleanup

DataTTL
Stale marker (no heartbeat)30 seconds
Offline instance reclaim60 seconds
Messages1 hour
Completed/failed/cancelled tasks24 hours
Events24 hours
Orphaned progress/ + plan/<instance-id> KV1 hour

When a session reaches the offline reclaim window, claimed or in-progress tasks are released back to open and that session's file locks are removed.

File locks stay exclusive and are cleared when the owning instance is reclaimed offline, deregisters, or completes the owning task.

Run swarm-mcp cleanup --dry-run --json to inspect what the janitor would remove without mutating the shared database.


Tools

Instance registry

ToolDescription
registerJoin the swarm. Starts heartbeat + notification poller. See Registration fields.
deregisterLeave the swarm gracefully. Releases tasks and locks.
bootstrapYield-checkpoint read for current instance, peers, unread messages, tasks, and configured work tracker metadata.
swarm_statusCompact coordination summary: peers, unread messages, assigned/claimable tasks, locks, warnings, planner ownership, and suggested next action.
list_instancesList all live instances.
remove_instanceForcefully remove another instance. Releases its tasks and locks.
whoamiGet this instance's swarm ID.

Messaging

ToolDescription
send_messageSend a direct message to a specific instance by ID.
prompt_peerSend a durable swarm message, then best-effort wake the target's workspace handle. Busy handles are not interrupted unless forced.
peek_peerRead recent or visible terminal text from a target's published workspace handle when the backend supports it.
resolve_workspace_handleMap a transport-local workspace handle, such as a herdr pane, back to a swarm instance ID.
broadcastMessage all other instances in the swarm.
poll_messagesRead unread messages and mark them as read.
wait_for_activityBlock until new messages, task changes, KV changes, or instance changes arrive. Use only while actively monitoring a peer/dependency/review/lock, not as a generic idle loop.

Task delegation

ToolDescription
request_taskPost a task (types: review, implement, fix, test, research, other). Use review for routine code review handoff. Supports priority, depends_on, idempotency_key, parent_task_id, review_of_task_id, fixes_task_id, and approval_required.
request_task_batchCreate multiple tasks atomically in a single transaction. Supports $N references (1-indexed) for dependencies, parent links, review links, and fix links.
dispatchGateway-only: create/reuse a task, wake a matching live worker, or spawn through the configured spawner backend. Ordinary workers should not call this. Pass completion_wait_seconds only when the caller wants to wait for terminal task completion; default dispatch returns immediately after handoff/spawn.
claim_taskStart work on a specific task: assigns and transitions to in_progress in one call. Prevents double-claiming and blocks on unread messages until poll_messages (or explicit override). Also accepts tasks pre-assigned to you (status=claimed).
claim_next_taskAtomically pick and claim the highest-priority compatible task. Prefers tasks pre-assigned to you, then open unassigned tasks. Optional filters support task types and overlapping files.
report_progressUpdate first-class progress fields on an in_progress task, including optional blocked_reason and expected_next_update_at. Use for multi-minute or blocked work.
complete_taskComplete a claimed task with structured JSON result fields: summary, files_changed, tests, and followups. Prefer this over update_task when you can provide structured handoff details.
update_taskMove a task to a terminal status (done, failed, cancelled). Auto-releases the actor's locks on the task's files. Use as a plain-string fallback or when structured completion is not useful.
approve_taskApprove a task in approval_required status. Transitions to open/claimed (or blocked if deps unmet).
get_taskGet full details of a task.
list_tasksFilter tasks by status, assignee, or requester. Sorted by priority (highest first).

File locking

ToolDescription
get_file_lockRead active lock state for a file without acquiring a lock.
lock_fileAcquire a file lock. Re-entrant for the same instance by default; pass exclusive=true to conflict on any existing lock (including same-instance) for one-shot mutexes like spawn coordination. Locks auto-release on terminal update_task or complete_task.
unlock_fileRelease a file lock early (before the task as a whole completes).

Key-value store

ToolDescription
kv_getGet a value by key.
kv_setSet a key-value pair visible to all instances.
kv_appendAtomically append a JSON value to a KV array.
kv_deleteDelete a key.
kv_listList keys, optionally filtered by prefix.

CLI

The same swarm-mcp binary exposes a non-MCP CLI that talks directly to ~/.swarm-mcp/swarm.db. Use it from contexts that cannot speak MCP: shell scripts, helper scripts an agent invokes (e.g. a test harness or CLI referee), cron jobs, CI, an ad-hoc terminal for inspection/debugging, or to control a running swarm-ui app.

Inside an MCP-enabled agent session, prefer the MCP tools for swarm coordination primitives (register, messages, tasks, locks, KV). The CLI is primarily for scripts, operator terminals, and the swarm-ui control surface.

Launcher-managed sessions may set SWARM_MCP_BIN to a real command such as bun run /path/to/swarm-mcp/src/cli.ts. Agents should use that prefix instead of assuming swarm-mcp is installed on PATH.

Setup helper:

swarm-mcp init --dir /path/to/project   # write .mcp.json and copy the packaged swarm-mcp skill
swarm-mcp init --no-skills              # write only the MCP config

init writes a project .mcp.json entry that runs npx -y swarm-mcp and, unless --no-skills is passed, copies skills/swarm-mcp into .claude/skills/. Manual host-specific MCP configs are still useful when your host does not read .mcp.json or you want to run from a local clone.

Inspection:

swarm-mcp inspect                    # unified dump of instances, tasks, kv, locks, recent messages
swarm-mcp inspect --scope /path      # pin to an explicit scope
swarm-mcp doctor                     # health report: binary, db, scope, skill/plugin install, env knobs (--json supported)
swarm-mcp messages --from <who>      # peek (does not mark read)
swarm-mcp cleanup --dry-run --json   # inspect cleanup without deleting
swarm-mcp kv list --prefix pixel:
swarm-mcp kv get pixel:turn

Writes (require identity — pass --as <uuid | prefix | unique-label-substring> or set SWARM_MCP_INSTANCE_ID; falls back to the sole live instance in scope):

swarm-mcp send --to <who> "message text"
swarm-mcp broadcast "status update"
swarm-mcp kv set  <key> <value>
swarm-mcp kv append <key> <json-value>
swarm-mcp kv del  <key>
swarm-mcp lock    <file> --note "why"
swarm-mcp unlock  <file>

Swarm UI control:

swarm-mcp ui spawn /path/to/repo --harness codex --role planner
swarm-mcp ui prompt --target role:planner "check the failing tests"
swarm-mcp ui move --target bound:<instance-id> --x 120 --y 80
swarm-mcp ui organize --kind grid
swarm-mcp ui list

These commands enqueue work for a running swarm-ui app to claim and execute. If no desktop app is running, commands remain pending until one starts.

Notes:

  • swarm-mcp ui spawn, ui prompt, ui move, and ui organize wait up to 5 seconds by default for the desktop app to claim + complete the command. Pass --wait 0 to return immediately after enqueue.
  • swarm-mcp dispatch returns immediately after task handoff/spawn by default. Pass --wait-for-completion <seconds> when a gateway wrapper should wait for the task to become done, failed, or cancelled; JSON output includes a completion object with the terminal task or timeout snapshot.
  • ui spawn --harness <name> accepts any launcher alias defined for the calling profile (see env/launchers.zsh.example), or the canonical names claude / codex / opencode / hermes directly. Omit --harness for a plain shell. Pick the alias whose profile matches the worker you intend to spawn — see identity boundaries.
  • dispatch normalizes spawned worker harnesses through the requester's identity. The requester's profile env declares SWARM_HARNESS_CLAUDE / _CODEX / _OPENCODE / _HERMES aliases; generic harness requests resolve to those aliases at spawn time.
  • Identified dispatch / ui spawn callers must be registered with a mode:gateway label. Trusted operator shells can bypass that accidental-use guard with SWARM_MCP_ALLOW_SPAWN=1.
  • Use swarm-mcp ui list and swarm-mcp ui get <id> to inspect queued, running, completed, or failed UI commands.
  • --target accepts bound:<instance-id>, instance:<instance-id>, pty:<pty-id>, or a bare instance / PTY reference. Bare instance refs resolve by full UUID, unique UUID prefix, or unique label substring in scope. Bare PTY refs resolve by full PTY id, unique PTY id prefix, or a unique substring of the PTY command.
  • ui move persists layout into the shared ui/layout KV entry for the target scope, so changes survive refreshes and can be driven from either the desktop UI or the CLI.
  • ui organize currently supports only --kind grid.

State, write, and UI subcommands accept --json for machine-readable output where shown by swarm-mcp help.

Canonical helper-script pattern — a harness the agent invokes to do validation + state update + handoff in one shot:

// harness.mjs — run as `node harness.mjs <partner-id>` by an agent
import { execFileSync } from "node:child_process";
const me = process.env.SWARM_MCP_INSTANCE_ID;
const scope = process.env.SWARM_MCP_SCOPE;
// ... validate and write artifacts ...
execFileSync("swarm-mcp", ["kv", "set", "turn", JSON.stringify(next), "--scope", scope, "--as", me]);
execFileSync("swarm-mcp", ["send", "--to", partner, "your turn", "--scope", scope, "--as", me]);

Security note: --as trusts the caller. The CLI will write as any live instance. Do not expose this binary to untrusted callers — the security model is the same as the underlying shared SQLite file.


Resources

The server exposes 4 MCP resources. swarm://inbox, swarm://tasks, and swarm://instances are refreshed by the background poller when the host supports resource update notifications.

URIDescription
swarm://inboxUnread messages for this instance.
swarm://tasksTasks grouped by status, including open, claimed, in-progress, blocked, approval-required, done, failed, and cancelled.
swarm://instancesAll active instances.
swarm://lock?file=...Active lock state for a specific file.

Prompts

The server exposes MCP prompts. Some hosts surface them directly, while others only expose tools and resources.

PromptPurpose
setup (often shown as swarm:setup)Guides the agent through registration: call register, then bootstrap, then summarize swarm ID, active sessions, role labels, open tasks, and coordination risks.
protocol (often shown as swarm:protocol)Applies the recommended coordination workflow for the session: inspect lock state, use lock_file for deliberate critical sections, use messages/tasks for handoff, and inspect role: labels when choosing collaborators.

Coordination doctrine

For autonomous collaboration, agent doctrine lives in the bundled swarm-mcp skill rather than in copy-paste AGENTS.md snippets. The skill carries a short main SKILL.md plus on-demand references for each role:

On hosts that support installable skills, invoke /swarm-mcp planner, /swarm-mcp implementer, etc. On hosts without skill support, point your AGENTS.md (or equivalent) at skills/swarm-mcp/SKILL.md — it doubles as a readable doctrine file.

For runtime-agnostic routing doctrine that should always be loaded (not on-demand), see docs/agent-routing.md. Runtime plugins (integrations/hermes/, integrations/claude-code/) automate registration, locking, and the /swarm slash command on top of the skill.

If your host exposes MCP prompts, you can also use the built-in protocol prompt (often shown as swarm:protocol) to pull the workflow into a session on demand.

Skills

This repo ships the reusable consumer skill at skills/swarm-mcp. Repo-internal skills live under .agents/skills and are not packaged by default.

Use swarm-mcp when your host supports installable SKILL.md workflows and you want agents to learn the swarm protocol more reliably. Invoke role-specific workflows with /swarm-mcp planner, /swarm-mcp implementer, /swarm-mcp reviewer, or /swarm-mcp researcher. For install locations, see docs/install-skill.md.

Use skills in addition to minimal always-on instructions, not instead of them. A skill is a playbook; AGENTS.md is still the best place for ambient rules like "register early" and "check locks before editing."

The skills do not mount the MCP server for you. They assume the swarm MCP tools are already available in the session.


Troubleshooting

Sessions can't see each other. Check that both sessions registered with the same scope (or both defaulted to the same git root). Verify they are using the same database path (~/.swarm-mcp/swarm.db by default). Run list_instances in both sessions.

Tools aren't available after config change. Most hosts only load MCP server changes at startup. Restart the application or start a fresh session after editing the MCP config.

File locks are stuck. Stale locks are cleared automatically when the owning instance's heartbeat expires (30s). If you need to clear them manually, delete the row from the context table in the SQLite database, or restart the stuck session.

Inspecting the database directly. The database is a standard SQLite file at ~/.swarm-mcp/swarm.db. You can open it with any SQLite client (bun itself, sqlite3, DB Browser for SQLite, etc.) to inspect instances, tasks, messages, locks, and KV.

Wrong absolute path in server command. The bun run command needs an absolute path to src/index.ts. Relative paths may resolve differently depending on how the host launches the process.


Security

All sessions on the same machine share one SQLite file. Any process running as the same OS user can read and write to it. There is no authentication or authorization between sessions.

This is intentional for a local development tool. Do not use swarm-mcp across trust boundaries or expose the database to untrusted users.


License

MIT

Image 1
1 / 1