Skip to main content
Glama
README.md
# Tower πŸ—Ό

[![CI](https://github.com/Rohanxmalik/Tower/actions/workflows/ci.yml/badge.svg)](https://github.com/Rohanxmalik/Tower/actions/workflows/ci.yml)
![Node β‰₯22.13](https://img.shields.io/badge/node-%E2%89%A522.13-3fb950)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)

**Multiplayer for your team's AI coding agents.**

**[tower-mcp on npm](https://www.npmjs.com/package/tower-mcp)** Β· **[Website](https://rohanxmalik.github.io/Tower/)** Β· **[Docs](./docs/quickstart.md)** β€” setup: `npx -y tower-mcp setup`

Every great work tool went multiplayer β€” Docs beat Word, Figma beat Photoshop. AI is
still the one everyone uses alone: one prompt, one box, one person.

Tower is an [MCP](https://modelcontextprotocol.io) server that turns your team's coding
agents β€” Claude Code, Cursor, Codex, on different machines and different accounts β€” into
**one crew on one repo**. Your agent **delegates a task** to your teammate's agent; theirs
does the work with _their_ tokens, commits it, and **reports back with the sha** β€” and the
task, the reply and the sha all land on **one shared board** the whole team can see,
instead of a thousand private threads. And because everyone declares intent before editing,
no two agents ever burn tokens on the same code β€” collisions are held **before the first
keystroke**, not found at merge.

```
YOUR MACHINE β€” alice                          THEIR MACHINE β€” bob
──────────────────────────────                ──────────────────────────────
you: "swap JWT for sessions;
      hand rate-limiting to bob"
agent β†’ send_message (task)      ─────────►   agent claims src/auth.ts
                                              β†’ "unreadMessages: 1" β†’ fetch_messages
                                              (or: tower work accepted it, running claude…)
                                              β†’ does the task, their machine/tokens
                                              β†’ commits (hook completes the claim)
[DONE] bob β†’ alice:              ◄─────────   β†’ send_message (task_update)
"rate limit 30/min, merged in ab12f3"
```

![Tower live board β€” a delegated task, a reply, and a prevented collision](docs/board.png)

> Status: **v0.12.1 β€” early, building in public.** Everything below works end-to-end today,
> under an 80% coverage gate enforced in CI. What's shipped and what's next:
> [CHANGELOG.md](./CHANGELOG.md) Β· design doc: [MVP-SPEC.md](./MVP-SPEC.md).

## Why

Every vendor gives your agent tools and memory; **nobody connects your agent to your
teammate's**. Two people, ten agents, one codebase β€” and the agents can't see each other,
can't hand off work, and collide on the same files with nothing to show for it but a merge
conflict. Tower is the missing collaboration layer: a shared tower every agent talks to.
It sits _above_ git and _uses_ MCP; it doesn't replace either. Model-agnostic by
construction β€” coordination only matters if the _other_ vendor's agent is in the room.

## The six words you need

| Word            | What it means here                                                                                                                                                                    |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **MCP**         | the standard way agents call external tools. Claude Code, Cursor and Codex all speak it β€” so Tower works with all of them, no plugin                                                  |
| **claim**       | an agent saying _"I'm about to edit these files/functions"_ **before** it edits. The core move                                                                                        |
| **symbol**      | a named function, class or method β€” so two agents editing _different_ functions in one file aren't treated as colliding                                                               |
| **hard / soft** | `hard` = same symbol β†’ the claim is **refused**, with what to work on instead (`force` overrides). `soft` = same file different symbols, or another branch β†’ you're told, you proceed |
| **board**       | a web page showing every agent's active claims, tasks and messages                                                                                                                    |
| **worker**      | `tower work` β€” a daemon on a machine that picks up delegated tasks and runs a coding agent headlessly                                                                                 |

Claims expire on their own (15-minute TTL, refreshed by heartbeats), so a crashed agent
never locks a file forever β€” and a **live** agent's claims are extended automatically, so
they don't lapse mid-task.

**One repo means one coordination space.** Tower keys claims on the repository's _root
commit sha_, which every clone, fork and mirror shares. So a fork and its upstream
coordinate with each other, and `git@github.com:acme/app.git` and
`https://github.com/Acme/App` are the same place rather than two isolated groups.

## See it β€” 30 seconds

Needs **Node 22.13+** (it uses the built-in `node:sqlite`, so there's nothing to compile).
Nothing to install, nothing written to disk:

```bash
npx -y tower-mcp demo
```

**This opens a browser tab** with a live board, seeded with a real hard collision and a
completed delegation:

```
Tower demo is live β†’ http://localhost:52410/board#token=demo

What you're seeing: alice and bob both claimed AuthService.verify β€” a HARD
collision caught before either wrote a line. Below it: a delegated task with
bob's reply + commit, a broadcast still waiting, and a pinned team rule.
Ctrl+C stops the demo (nothing was written to disk).
```

Didn't work? `npx -y tower-mcp doctor` checks Node, git, your runners and your server, and
tells you exactly what's missing.

<details><summary>Prefer a terminal-only demo? (needs a clone)</summary>

```bash
npm run demo    # after: git clone … && npm install
```

```
β›” COLLISION β€” AuthService.verify
   Agent "cursor-bob" is mid-change (started 2s ago, ETA ~6m, purpose: replace JWT).
   Options:
     [w] wait      β€” no need to retry: Tower messages you when their claim ends
     [d] dependent β€” run: tower next-task  (a module that's safe to start now)
     [b] branch    β€” build on their WIP instead of racing them
     [f] force     β€” re-run guard with --force; you own the merge risk
```

</details>

## Install it in your repo

**What `setup` writes** β€” all of it in your repo, nothing global, no network calls you
didn't configure:

| Path                                   | What                                                                |
| -------------------------------------- | ------------------------------------------------------------------- |
| `.mcp.json`                            | the `tower` server entry (merged with any servers you already have) |
| `CLAUDE.md`                            | the claim-first rule appended (created if you don't have one)       |
| `AGENTS.md`                            | same rule appended β€” only if the file already exists                |
| `.tower/`                              | your local SQLite state (claims, messages, tasks). Safe to delete   |
| `.gitignore`                           | `.tower/` appended, so you never commit the local db                |
| `.git/hooks/pre-commit`, `post-commit` | **only with `--hooks`**                                             |

```bash
npx -y tower-mcp setup
```

Reload your editor β€” done. Add `--keep-going` and the rule it writes tells agents to
**route around a conflict** rather than stop and ask you (see [Nobody waits](#nobody-waits)).
Joining a team server instead?

```bash
npx -y tower-mcp setup --url https://tower-xxxx.onrender.com/mcp --token <team-secret> --hooks
```

<details><summary>What setup does / manual config</summary>

`setup` writes the `tower` entry into `.mcp.json` (merging with your existing servers),
appends the claim-first + check-your-inbox rule to `CLAUDE.md` (and `AGENTS.md` if you
have one), and with `--hooks` installs the git pre/post-commit guards. Manual equivalent:

```jsonc
// Claude Code β€” .mcp.json
{
  "mcpServers": {
    "tower": { "command": "npx", "args": ["-y", "tower-mcp", "serve"] },
  },
}
```

```bash
npx -y tower-mcp init      # writes .tower/policy.yaml + prints MCP setup
npx -y tower-mcp serve     # MCP over stdio (or: serve --http --port 4319 --token <secret>)
```

</details>

<details><summary>From source (contributors)</summary>

```bash
git clone https://github.com/Rohanxmalik/Tower && cd Tower
npm install && npm run build
node packages/cli/dist/index.js serve
```

</details>

Then add to your agent's rules file:

> "Before editing any file, call `claim_intent` with the files and symbols you'll change.
> If a `hard` conflict returns, stop and ask the user; the response's `alternatives` says
> what is safe to work on meanwhile."

Full setup β†’ [docs/quickstart.md](./docs/quickstart.md).

## Delegate work across machines (the core loop)

Two people, two machines, two accounts β€” one repo. This is what Tower is for:

1. **Delegate** β€” you tell your agent _"hand the rate-limiting work to bob"_ (or it decides
   itself, per your rules): it calls `send_message` with `kind: "task"`. Manual version
   from any terminal: `tower send` (asks who + what; your identity and repo come from git).
2. **Pick up** β€” the next time bob's agent touches Tower (any `claim_intent`), the response
   says `unreadMessages: 1`; the rules file tells it to `fetch_messages` and act. Delivery
   is inbox-style β€” MCP has no push channel β€” so it's asynchronous, like Slack, not a
   phone call. **Or make it always-on:** with `tower work` running on bob's machine, the
   pickup is automatic β€” the worker accepts the task and runs a local agent headlessly,
   no editor needed.
3. **Do the work β€” their machine, their account.** Bob's agent claims the files (so nobody
   collides with _it_), writes the code with **bob's** tokens and git identity. No API keys
   ever cross machines.
4. **Commit & close the loop** β€” on commit, the git post-commit hook completes the claim
   with the sha; the agent replies `send_message { kind: "task_update", replyTo: <task> }`:
   _"rate limit 30/min on /login, merged in ab12f3."_ Your agent sees it on its next
   contact β€” and the whole exchange is on the board's COMMS panel the whole time. Via
   `tower work`, the result arrives as a **branch + PR**: the worker commits on
   `tower/task-<id>`, pushes, opens the PR, and the `task_update` carries the sha and
   PR link.

**Always-on delegation:** `npx -y tower-mcp work` turns any machine into a task worker β€”
it polls for delegated tasks, confirms with you (or runs unattended with `--auto`), drives
`claude -p` / `codex exec` headlessly, and PRs the result. Full guide + security model β†’
[docs/worker.md](./docs/worker.md).

**A worker needs three things** (run `npx -y tower-mcp doctor` and it checks all of them):
a **clean git working tree** β€” it refuses to run on uncommitted changes, so it never mixes
your work into a task's commit; `claude` or `codex` **on PATH**; and authenticated `gh` if
you want pull requests (without it, branches still push).

Trust model, plainly: an inbound task is code your teammate's agent will act on β€” treat the
shared `TOWER_TOKEN` like push access, and agents should confirm out-of-scope tasks with
their human ([SECURITY.md](./SECURITY.md)).

## Drive it from your phone

The board is a remote control, not just a dashboard. Open `https://<your-tower>/board` on
your phone:

- **Send box** β€” delegate a task in one line. Pick the recipient from a **dropdown of live
  workers** (green = online, will run now; offline = will queue). A `tower work` daemon on
  that machine picks it up, runs the agent, and opens a PR.
- **Approve / Reject** β€” run the worker with `--approve remote` and it parks each task
  instead of asking a terminal. Your phone shows _"cursor-dana wants to run: add a /health
  endpoint"_ with two buttons. Tap Approve; your laptop does the work.
- **Map view** β€” a command-flow tree: who directs whom, each task and its reply, live
  presence dots. **Tap any agent to command it.** ([docs/map.png](./docs/map.png))
- **One-tap auth** β€” open `/board#token=<token>` and it connects with no typing.

Same `TOWER_TOKEN` as everything else β€” anyone who can open your board can drive your
worker, so share it like push access. Details β†’ [docs/worker.md](./docs/worker.md).

## Catch duplicate work before it happens

The expensive failure isn't usually a merge conflict β€” it's two agents doing the **same
work**. They pick different filenames, git merges both cleanly, and you've paid for one
deliverable twice. No file-level check can see that, and by the time either agent touches
a file the tokens are already spent.

So before researching anything, an agent says what it's about to do:

```
propose_intent  "write a blog post about prompt injection"

⚠️  claude-bob is already on this (2m ago):
    "AI agent security / prompt injection"
    β†’ stand down, or pick something else
```

Matched on meaning, not paths β€” it fires even though one agent would have written
`prompt-injection-agent-security.mdx` and the other
`ai-agent-security-prompt-injection.mdx`. Entirely local: no model, no embeddings, no
network call.

## Catch the contract that moved under you

A claim is only as fresh as the read that produced it.

Comparing what two agents will **write** catches them editing the same function. It is
blind to the more common failure: alice changes `AuthService.verify` in `auth.ts` while
bob, who read the old signature ten minutes ago, writes a caller in `payments.ts`.
Different file, different symbol β€” nothing overlaps, both are told to proceed, and bob
finds out at CI.

So a claim also carries what it was **built on**:

```
claim_intent  files: ["src/payments.ts"]  symbols: ["charge"]

[HARD] AuthService.verify moved under you β€” alice changed the declaration you read
    was: verify(token: string)
    now: verify(token: string, opts: Opts)
```

Two details make this worth having switched on:

**It shows you the delta, not a warning.** Telling an agent "your context may be stale,
re-read the file" costs a whole module back in context to discover one parameter moved.
Two lines replace it, and the agent patches its call sites without reopening anything.

**It only fires when a caller could actually break.** The fingerprint covers the
_declaration_, never the body β€” so a rewritten implementation, a renamed local, a comment
or a `prettier` run move nothing. An added parameter or a changed return type always
does.

You don't have to declare what you read: the `PostToolUse` hook watches `Read` and
records it for you, with each declaration's signature at the moment you looked. And if
you claimed first and something moved afterwards, `heartbeat` tells you β€” on the call
your agent already makes every 60 seconds.

Honest limits: a behavioural change under an identical signature is invisible, which is
the trade that keeps false positives near zero. See [docs/protocol.md](docs/protocol.md).

## Nobody waits

A refusal used to be a full stop: `stand_down`, and a rule telling the agent to ask you.
One conflict parked an agent until a human noticed. As of 0.12.0 a conflict is a
**detour**:

**The refusal says what's still safe.** It carries `alternatives` β€” what to stay out of,
and one line of advice:

```
(claim REFUSED β€” another agent holds this. Re-run with --force to override.)
What to do instead:
  Held by bob (~10 min). Don't wait: work on anything outside the 2 symbols in
  `avoid`. Tower will message you when it frees up β€” no need to retry.
  Avoid for now: AuthService.verify() (src/auth.ts), charge() (src/payments.ts)
```

`charge` is in there because Tower **inferred** it: an earlier claim wrote `charge`
having read `verify`, so `charge` depends on it. No dependency map to write β€” it comes
from what agents actually read.

**Nobody polls.** The moment bob's claim completes, is released or expires, the agent
that was refused gets a message from `tower` saying what freed up.

The same holds when the block comes from the PreToolUse hook or the git pre-commit guard
(as of 0.12.1): the blocked agent sees the alternatives and gets the message when the
claim frees up.

**You find out when you read, not when you edit.** Open a file someone is changing and
the `PostToolUse` hook tells your agent right then β€” before it plans anything on top of
code that's about to move.

**Dependent work runs in parallel β€” contract-first.** If alice is changing a signature,
she says what it will become:

```
claim_intent  symbols: [{ symbol: "AuthService.verify",
                          declares: "verify(token: string, opts: Opts): boolean" }]
```

Bob, writing a caller, is handed that contract β€” not just a warning that `verify` is
moving β€” and codes against it now instead of waiting for alice to land. When alice completes, Tower checks what actually landed and tells bob whether it
matches what she declared β€” so if the plan changed, he hears it from Tower, not from CI.

**Let agents keep going on their own.** `tower setup --keep-going` writes a rule that
says: on a conflict, work outside `avoid` and only ask the human if nothing safe is left.
The default rule still says stop and ask β€” waiting on a person is the conservative
choice, and it stays the default.

## What actually collides

```
$ tower stats

2 collision(s) recorded

  by kind
    write_write  1  (50%)  two agents on the same symbol
    write_read   1  (50%)  a contract moved under a reader
```

Counts only β€” no file names, no symbol names, no code, and nothing leaves your machine.
It exists because Tower spent four versions detecting collisions and forgetting every
one, so nobody could say which kind actually happens.

## The 20 tools

| Tool                                           | Purpose                                                                     |
| ---------------------------------------------- | --------------------------------------------------------------------------- |
| `claim_intent`                                 | Register intent **and** get collisions in one call (primary)                |
| `check_collision`                              | Dry-run collision check, no claim persisted                                 |
| `heartbeat`                                    | Keep a claim alive (auto-expires otherwise)                                 |
| `complete_claim` / `release_claim`             | Free a claim on commit / abandon; waiting agents are told it's free         |
| `list_claims`                                  | Live claim state                                                            |
| `log_decision` / `get_decisions`               | Shared architecture-decision memory                                         |
| `next_task`                                    | Rule-based sequencer: a module that's safe to start now                     |
| `send_message` / `fetch_messages`              | The agent channel: async messages + **task delegation** between agents      |
| `pending`                                      | Read-only count of unread messages + open tasks waiting for you (the nudge) |
| `accept_task` / `complete_task` / `list_tasks` | Task lifecycle: first-accept-wins assignment, results with sha/PR           |
| `request_approval` / `resolve_approval`        | Human-in-the-loop gate: park a task, approve it from the board/phone        |
| `heartbeat_worker`                             | Live presence β€” a worker announces it's online & ready to run tasks         |
| `propose_intent`                               | **Before you research:** say what you plan to do; catches duplicate work    |
| `record_reads`                                 | What you just read β€” and who is changing it right now (the hook calls it)   |

Wire contract β†’ [docs/protocol.md](./docs/protocol.md).

## How it works

```
MCP clients (Claude Code / Cursor / Codex)
        β”‚  stdio  Β·  HTTP/SSE
        β–Ό
Tower server ── collision engine (tree-sitter) Β· agent inbox Β· sequencer Β· SQLite Β· /board UI
        β–²
tower CLI: demo Β· doctor Β· init Β· setup Β· serve Β· status Β· stats Β· watch Β· complete Β· claim Β· guard Β· next-task Β· send Β· inbox Β· nudge Β· work Β· version
```

- **Semantic, not textual:** symbols come from tree-sitter ASTs (TS/JS/Python), so
  `AuthService.verify` collides even across different diff hunks.
- **Model-agnostic:** it's an MCP server β€” every major agent works today.

## Enforcement (don't rely on the agent remembering)

A tool call the agent _chooses_ to make isn't a safety net. Tower has **three enforcement
layers** β€” stack them:

1. **MCP tools + rules file** β€” every agent (Claude, Cursor, Codex) claims before editing.
   `tower setup` writes this for you.
2. **Claude Code hooks** β€” a conflicting `Edit`/`Write`/`MultiEdit` is physically
   **blocked**. ⚠️ **Needs a clone of Tower today** β€” the hook scripts aren't in the npm
   package; they run from the clone and import its built CLI:
   ```bash
   npm install && npm run build
   npx tower-mcp init --hooks   # writes .claude/settings.json, then reload Claude Code
   ```
   That wires five hooks: **SessionStart** registers the session, **UserPromptSubmit**
   tells you when tasks or messages are waiting, **PreToolUse** blocks a hard-conflicting
   edit (exit 2), **PostToolUse** keeps your presence alive on edits and, on `Read`,
   records what you read and warns you if someone is changing it, **SessionEnd** releases
   your claims. **Upgrading** an existing install is the same command β€”
   `git pull && npm install && npm run build && npx tower-mcp init --hooks` β€” which
   rewrites Tower's own entries to the current version and never changes your own hooks.
   Re-running it changes nothing.
3. **Universal git pre-commit guard** β€” works with _any_ editor or agent; the commit itself
   is refused while a teammate's agent holds a conflicting claim:
   ```bash
   cp examples/git-hooks/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
   ```

**How precise the hook is:** it locates each edit and claims the **function** it lands
in, so two agents in different functions of one file don't block each other. A `Write`,
a new file, or an edit between declarations (imports, top-level code) falls back to the
whole file β€” over-claiming is safe, under-claiming is not. Full scope and limits β†’
[docs/enforcement.md](./docs/enforcement.md).

## The live board

Every `serve --http` Tower ships a live board at **`/board`** (it refreshes every 2s):
every agent's active claims, collisions flagged red, how long each claim has been open β€”
and the **COMMS panel** showing every message and delegated task as it lands. Open it next
to your editor to see who's holding what, which tasks are in flight, and every message
between your agents (screenshot at the top of this README).

> To be clear about what this is: the board shows **claims, tasks and messages** β€” not a
> live transcript of an agent's session. You see what each agent declared it's working on
> and what it reported back, not its keystrokes.

The agent channel from a terminal β€” just run `send`; it asks the rest (who you are + the
repo come from git):

```
$ npx -y tower-mcp send
To (agent id, or * for everyone): bob
Message: add rate limiting to /login
Is this a task for them? [y/N]: y
πŸ“¨ Sent task c78094d1 β†’ bob

$ npx -y tower-mcp inbox         # your messages (identity inferred from git)
```

(Scripts/agents pass flags instead: `send --to bob --body "..." --task` β€” prompts
never appear outside a real terminal.)

## GitHub Action: PR collision reports

No server needed β€” one workflow file comments on any PR that touches the same files
(overlapping lines flagged) as another open PR, and shows live agent claims if you run a
hosted Tower:

```yaml
- uses: Rohanxmalik/Tower/action@v0.12.1
```

Pin a release tag like this; `@main` works too but tracks the bleeding edge. Setup + screenshots β†’ [docs/action.md](./docs/action.md).

## Team mode (whole team, different machines)

Point everyone's agents β€” Claude, Cursor, Codex β€” at **one** Tower. When two people's
agents reach for the same file, the second is flagged **before it spends a token** β€” not at
merge. Two setups, pick by your team:

- **Same office / same WiFi (or living together):** no deploy, no tunnel β€” one laptop hosts
  (`serve --http --host 0.0.0.0`), everyone points at its `192.168.x.x` address. 2 minutes.
- **Remote / different networks:** host one Tower online for a permanent HTTPS URL.

Deploy your own online in ~5 minutes (free tiers available), no tunnels:

[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/Rohanxmalik/Tower)

Or self-manage with Docker:

```bash
TOWER_TOKEN=your-secret docker compose up -d   # http://<host>:4319/mcp
```

Each dev's `.mcp.json` uses `"type": "http", "url": ".../mcp"` β€” now your Claude tells your
co-founder's Codex "don't touch auth until commit abc123." Full setup, beginner-friendly β€”
same-WiFi mode + click-by-click Render steps + per-editor config β†’
[docs/team.md](./docs/team.md).

> πŸš€ **Don't want to host it?** [Tower Cloud](https://rohanxmalik.github.io/Tower/#cloud) β€”
> a managed, always-on coordination server for teams β€” is coming. Join the waitlist.

## Trust, data, and how to remove it

- **No telemetry.** Tower makes no network calls except the ones you configure. The board
  page loads zero external resources β€” no CDN, no fonts, no analytics.
- **Your data stays in your repo.** Claims, messages, tasks and decisions live in
  `.tower/tower.db` β€” a plain SQLite file. Delete the folder and it's gone.
- **No API keys cross machines.** A worker shells out to the `claude` / `codex` already
  installed on _that_ machine. Tower never sees a vendor credential.
- **`TOWER_TOKEN` is a shared secret β€” treat it like push access.** Anyone holding it can
  delegate a task to a worker on your team. See [SECURITY.md](./SECURITY.md).
- **Tower never blocks you by failing.** Every hook fails _open_: if Tower is unreachable
  or a hook errors, your edit and your commit go through.
- **To remove it:** delete `.tower/`, drop the `tower` entry from `.mcp.json`, and delete
  `.git/hooks/pre-commit` and `post-commit` if you installed them with `--hooks`. If you
  ran `init --hooks`, also drop Tower's `node hooks/…` entries from `.claude/settings.json`.

## Monorepo layout

```
packages/shared   protocol types + zod schemas (source of truth)
packages/server   collision engine, sequencer, SQLite store, MCP server, transports
packages/cli      the `tower` command
hooks/            the five Claude Code hooks (SessionStart … SessionEnd; PreToolUse blocks)
action/           GitHub Action β€” PR collision reports
extensions/       tower-anywhere β€” claim-before-you-edit for non-code work (docs, briefs)
examples/         two-agents-demo, git-hooks (pre-commit guard, post-commit release)
docs/             quickstart, protocol, worker, enforcement, team, action
Dockerfile        hosted team server
```

## Develop

```bash
npm install
npm test          # vitest, 80% coverage gate
npm run build     # tsc -b
```

## Roadmap

- Per-agent identity & auth (today: one shared team token) β€” the Tower Cloud foundation
- More language grammars for symbol extraction (Go, Rust, Java β€” [contributions welcome](./CONTRIBUTING.md))
- Predictive conflict detection (ML on your merge history) β€” the eventual moat
- Auto-resolution / reconciliation agent
- Cross-repo / org-wide intent graph + API-contract break detection
- Enterprise: policy engine, SSO, audit ledger

## Contributing & community

PRs welcome β€” see [CONTRIBUTING.md](./CONTRIBUTING.md) (TDD, small PRs, good-first ideas
inside). Security reports β†’ [SECURITY.md](./SECURITY.md). Changes β†’ [CHANGELOG.md](./CHANGELOG.md).

## License

MIT

TDQS

B3.4/5.0

Scored across 20 tools

Disambiguation4/5

Most tools have clearly distinct roles, and the rich descriptions help separate the claim lifecycle (claim_intent/check_collision/release_claim/complete_claim) from intent proposals (propose_intent) and task flows. However, a couple of pairs invite confusion: heartbeat vs heartbeat_worker, and the trio claim_intent/propose_intent/check_collision all touch pre-work collision checking.

Naming Consistency4/5

The dominant pattern is predictable verb_noun (claim_intent, release_claim, complete_claim, list_claims, send_message, accept_task, complete_task, list_tasks, request_approval, resolve_approval). A few deviations stand outβ€”heartbeat, heartbeat_worker, pending, next_taskβ€”which break the otherwise clean convention.

Tool Count4/5

20 tools is on the heavier side, but the server legitimately spans several subdomains (claims, tasks, messaging, decisions, approvals, presence), so most tools earn their place. A little redundancy exists (pending duplicates part of fetch_messages/list_tasks; two heartbeat tools), but not enough to feel bloated.

Completeness4/5

Coverage is strong: full claim lifecycle, task delegation/accept/complete with approvals, messaging, decision memory, and presence. Minor gaps remainβ€”no explicit task-creation tool (folded into send_message with kind 'task') and no update/delete for recorded decisions.

Maintenance

ActivityActive
ResponsivenessNo issues