Skip to main content
Glama
README.md
# claude-broker

**A persistent, schema-enforced message bus for fleets of Claude Code sessions that must outlive a single run.**

`claude-broker` is a small [MCP](https://modelcontextprotocol.io) HTTP server backed by one
SQLite file. Point several [Claude Code](https://claude.com/claude-code) sessions (or any MCP
client) at it and they exchange messages over named channels. Messages persist, so a worker that
restarts picks up exactly where it left off. Channels can enforce a JSON Schema, so a malformed
result is rejected instead of silently confusing the next reader. A gated-message primitive lets
a human approve a step before it lands. Everything is queryable afterwards, so you can read what
happened a week later.

It is the coordination backbone behind long-running orchestrator/worker setups: one session
dispatching tasks, several sessions doing them, reviewer and judge sessions checking the work,
heartbeats, schema-validated protocols, guardrail hooks, and a live dashboard.

- **Transport** — Express + `@modelcontextprotocol/sdk` (Streamable HTTP)
- **Storage** — SQLite via `better-sqlite3` (WAL mode); a single file, no external services
- **Validation** — Ajv JSON Schema per channel (hot-reloadable) plus per-namespace manifests built from typed base envelopes
- **Auth** — shared bearer token, constant-time checked; separate admin and verifier credentials for destructive or trust-bearing operations
- **Extras** — server-side long-poll, server-side read cursors, a derived task ledger, capability registry, heartbeat/cost telemetry, an optional worker supervisor, PreToolUse guardrails, and an HTML dashboard

## Where it runs

This is the public cut of the broker I run my own companies on. Since May 2026 it has coordinated
**161 Claude Code sessions across 15 projects** (product engineering plus a marketing team). From
June to October 2026 it carried **1,117 dispatched tasks, 375 code reviews by reviewer agents and
154 questions escalated to a human** for a decision. Every incident in that period became a guard
and a regression test, which is why the repo ships with 40+ test suites. One example: a worker
session once ran `pkill` against the broker it was talking through and restarted it on the wrong
port, an 18-minute outage. That is now a denied command in [`hooks/guardrails.mjs`](hooks/guardrails.mjs)
and a case in `test-guardrail-hooks.js`.

The history was squashed for publication and client-specific namespaces were removed; the
broker's own self-maintenance namespace (`cb`) and two small example namespaces are kept as
worked examples.

## Architecture

```
            ┌────────────────────────┐   ?role=orchestrator&ns=cb
            │ orchestrator (Claude   │ ──────────────┐
            │ Code, interactive)     │               │
            └────────────────────────┘               ▼
                                            ┌──────────────────────────────┐
  ?role=worker&ns=cb&as=core                │  claude-broker  (server.js)  │
 ┌──────────────┐  dispatch_task / result    │  MCP over Streamable HTTP    │
 │ worker: core │ ◄────────────────────────► │  Ajv schemas + manifests     │
 └──────────────┘                            │  SQLite (WAL) + cursors      │
 ┌──────────────┐  APPROVE / ADVISE / BLOCK  │  long-poll, task ledger      │
 │ reviewer     │ ◄────────────────────────► │  REST + dashboard            │
 └──────────────┘                            └──────────────┬───────────────┘
 ┌──────────────┐  held questions, findings                  │
 │ judge / retro│ ◄──────────────────────────────────────────┘
 └──────────────┘          ▲
        watchdog.sh / runner/ ─ launches, heartbeats, rotates and relaunches each session
```

- **Channels and namespaces.** Every project gets a namespace prefix (`cb-`, `pg-`, …) with an
  inbox per worker plus `-orchestrator`, `-status`, `-control`, `-telemetry`, `-backlog` and
  `-notes` channels. A manifest ([`schemas/manifests/`](schemas/manifests/)) declares the
  workers, their roles and which base envelope types each channel accepts.
- **Typed envelopes.** `task`, `result`, `question`, `status`, `finding`, `decision`, `plan` and
  friends are JSON Schemas in [`schemas/base/`](schemas/base/). v3 tools (`dispatch_task`,
  `post_result`, `ask`, `post_note`) build and validate the envelope server-side so a session
  cannot post a malformed one.
- **Roles.** A session connects as `worker`, `reviewer`, `orchestrator` or `admin`; each role sees
  only the tools it needs (measured from real usage to keep the tool list small). Only a reviewer
  may post `APPROVE`/`ADVISE`/`BLOCK`; only the orchestrator may dispatch; purging a durable
  channel needs the admin secret.
- **Rules ratchet.** Agents can make a namespace stricter on their own (register a stricter
  schema, flip a channel to strict, add a guardrail); loosening a rule needs a human-held
  credential. The judge worker answers blocked questions only from recorded decisions and holds
  anything else for a human.
- **Durability and recovery.** Server-side cursors per (worker, channel) mean a replacement
  session resumes exactly where the last one stopped; a handoff status rewinds the cursor so the
  task is seen again; the watchdog relaunches on silent exit, timeout or crash.
- **Verification.** A result's `required_checks` can be re-run by an independent verifier process
  holding its own token; the broker stores the verdict next to the result.
- **Cost governance.** Heartbeats carry context size and model usage; the runner writes a
  per-task usage ledger, and an hourly spend patrol posts a finding (and at 10x an escalation
  question to the orchestrator) when a namespace's spend jumps against its 7-day baseline.
- **Retro.** A weekly retro compacts a namespace's findings and decisions into a ≤6 KB knowledge
  file every worker reads at cold start, and proposes role-file edits for the human to accept.

## Do you need this?

Claude Code already ships several ways to run more than one agent. Most multi-agent needs are
covered by those, and the broker adds nothing for them. Reach for it only when the last row fits.

| You want to… | Use |
|---|---|
| Fan work out inside one task (review five files, try three approaches) | Built-in subagents / the Workflow tool. The parent keeps the result. |
| Let two sessions on one machine trade a few notes this afternoon | Built-in `SendMessage` and teams. |
| Run something every morning or every ten minutes | `/loop`, `/schedule`, cron. |
| Coordinate **independent, long-lived** sessions that must survive restarts, follow a message contract, pause for human consent, and leave an audit trail | **claude-broker.** |

If your sessions are short and one of them owns the outcome, stay with the built-ins. If workers
run for hours or days, get restarted, and hand each other work that has to be well-formed, a
durable bus with schemas is the missing piece. That is what this is.

---

## Requirements

- **Node.js ≥ 20** (uses the native `better-sqlite3` addon — a C toolchain is needed at install
  time; macOS ships one with Xcode CLT, most Linux distros need `build-essential`/`python3`).

## Install

From source (the package is not on npm yet):

```bash
git clone https://github.com/anisthesia/claude-broker-oss.git
cd claude-broker-oss
npm install
```

## Quick start

```bash
# Configure — the wizard writes .env + workers.json and generates a secret.
# Point --project at the repo you want to coordinate (or omit for a bare setup).
npm run setup -- --project /path/to/your/project
# (manual alternative: cp .env.example .env, then set SHARED_SECRET)

npm start
```

You should see:

```
[claude-broker] v2.2.1 listening on :8080  auth:on  prune:48h  exempt:[]
[claude-broker] dashboard: http://localhost:8080/dashboard
```

Check it's alive:

```bash
curl -s localhost:8080/health
# {"ok":true,...}
```

> The broker **refuses to start without `SHARED_SECRET`**. For a purely local, trusted setup you
> can run unauthenticated by setting `BROKER_ALLOW_NO_AUTH=1`, but never do that on an exposed host.

## Connect a Claude Code session

Register the broker as an MCP server (the bearer token must match `SHARED_SECRET`):

```bash
claude mcp add --transport http broker http://localhost:8080/mcp \
  --header "Authorization: Bearer <your-SHARED_SECRET>"
```

Now, in any session, the broker's tools are available. Two sessions can coordinate:

```
# Session A
send_message(channel="team-status", sender="alice", content="build is green")

# Session B
wait_for_messages(channel="team-status", since_id=0)   # long-polls until A posts
```

## A 60-second tour

```
send_message      — post a message to a channel
read_messages     — read messages after a given id, or resume from your server-side cursor
wait_for_messages — server-side long-poll (up to 60s); prefer over busy-polling
list_channels     — see active channels and message counts
purge_channel     — clear a channel
```

That's the core. There are ~30 tools in total — messaging, results, schemas, capabilities,
heartbeats, and worker supervision. See **[docs/API.md](docs/API.md)** for the full reference.

## The dashboard

Open **`http://localhost:8080/dashboard`** in a browser. It shows channels, message volume,
worker/heartbeat state, and (if you post cost/rate-limit telemetry) per-worker cost rollups.
Because browsers can't set an `Authorization` header on a plain page load, dashboard routes also
accept the secret as `?token=<secret>` — convenient locally, but see
**[docs/SECURITY.md](docs/SECURITY.md)** before exposing it.

## Schemas (optional)

Any channel can enforce a JSON Schema on message `content`. Register one and non-conforming
messages are rejected (or warned about). Example schemas — a task/result worker protocol — live
in [`schemas/`](schemas/); see [`schemas/README.md`](schemas/README.md).

```
register_channel_schema(channel="team-backend", schema=<json-schema>, strict=true)
```

## Namespaces, roles and typed tools (protocol v3)

```bash
node setup.js --project /path/to/your/project --ns myapp --scaffold-roles   # wizard: .env, workers.json, manifest, role files
node setup-schemas-cb.js                                                   # or register a namespace's channel schemas by hand
```

Connect a session with its identity in the URL and it gets the typed tools for its role:

```
http://localhost:8080/mcp?role=worker&ns=myapp&as=backend
```

```
dispatch_task(to="backend", task_id="myapp-2026-11-01-a", subject="...", scope="small",
              checks=[{"name": "test", "run": "npm test", "pass_condition": "all pass"}])
post_result(task_id="myapp-2026-11-01-a", summary="PASS — ...", body={"required_checks": {...}, "commits": [...]})
ask(task_id="myapp-2026-11-01-a", question="A or B?")        # task shows as blocked until answered
get_task_ledger(status_channel="myapp-status", only_open=true)
```

The full design, including the plan gate, guardrails, model routing, verified checks and the
judge and retro workers, is in **[docs/protocol-v3.md](docs/protocol-v3.md)**. The broker's own
maintenance team (orchestrator, core, protocol-qa, reviewer, judge, retro) lives in
[`orchestrators/`](orchestrators/) and [`workers/`](workers/) as a worked example of the role files.

## Worker supervision (optional, advanced)

The broker can supervise long-running agent processes via `start_worker` / `stop_worker` /
`list_workers`, driven by a `WORKERS_CONFIG` file (see [`workers.example.json`](workers.example.json))
and an external supervisor script you provide (`WATCHDOG_BIN`). This is an advanced,
opt-in subsystem — it stays dormant unless you configure it. See
**[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md#worker-supervision)**.

## Configuration

All configuration is via environment variables (or `.env`). The full list, with defaults, is in
**[.env.example](.env.example)**. The essentials:

| Variable | Default | Purpose |
|---|---|---|
| `PORT` | `8080` | Listen port |
| `SHARED_SECRET` | — | Bearer token; **required** (or set `BROKER_ALLOW_NO_AUTH=1`) |
| `DB_PATH` | `./broker.db` | SQLite file |
| `PRUNE_MAX_AGE_MS` | 48h | Auto-delete chatter (heartbeats, status, notes) older than this |
| `PRUNE_SIGNAL_MAX_AGE_MS` | 30d | Retention for signal-type messages (the audit trail) |
| `PRUNE_EXEMPT` | — | Channels never auto-pruned (comma-separated) |

## Testing

```bash
npm test          # spins up a scratch broker on a temp DB and runs every suite (40+)
```

The runner (`run-tests.js`) never touches your live `broker.db`.

## Documentation

- **[docs/API.md](docs/API.md)** — every MCP tool and HTTP endpoint
- **[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)** — production deployment, TLS, systemd/launchd, backups
- **[docs/SECURITY.md](docs/SECURITY.md)** — auth model, threat model, hardening checklist
- **[CONTRIBUTING.md](CONTRIBUTING.md)** — how to contribute
- **[CHANGELOG.md](CHANGELOG.md)** — release history

## License

[MIT](LICENSE) © Anis Khan

Maintenance

ActivityMaintained
ResponsivenessNo issues