Skip to main content
Glama
0-to-1-Labs

Tracker MCP Server

by 0-to-1-Labs
README.md
<p align="center">
  <img src="docs/assets/hero.png" alt="Tracker — a real-time Kanban board where humans and agents ship together" width="960">
</p>

<h1 align="center">&gt; TRACKER</h1>

<p align="center">
  <strong>The real-time Kanban tracker where humans <em>and</em> agents are first-class principals.</strong><br>
  One domain layer, exposed over REST · WebSocket · MCP · webhooks — so an agent can drive a board with zero glue.
</p>

<p align="center">
  <a href="https://github.com/0-to-1-Labs/tracker/actions/workflows/ci.yml"><img src="https://github.com/0-to-1-Labs/tracker/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-AGPL--3.0-6fc3df.svg" alt="License: AGPL-3.0"></a>
  <img src="https://img.shields.io/badge/deploy-Docker%20Compose-2aaaff.svg" alt="Docker Compose">
</p>

---

Tracker is a collaborative Kanban board — boards → lists → cards, drag-and-drop, synced live
across every connected client. What makes it different is who counts as a *user*: an **AI agent
is an actor exactly like a human** — it can be a board member, be @mentioned, have cards
**delegated** to it, work them in a legible **session**, and ask a human when it's stuck. Every
mutation flows through one shared **domain function** that authorizes once, writes Postgres,
appends an append-only `Activity` row, and fans the event out over Redis. REST handlers, the
WebSocket gateway, the MCP tools, and outbound webhooks are all thin adapters over that single
spine — a contract test proves REST and MCP hit the *identical* path. Build for one surface and
you've built for all four.

<p align="center">
  <!-- Cross-client drag-and-drop reconciling live across two browsers. -->
  <img src="docs/assets/demo.gif" alt="Cross-client drag-and-drop: a card moved in one browser reconciles live in another" width="820">
</p>

## Feature tour

**Board experience.** Boards → lists → cards with fluid drag-and-drop (dnd-kit), optimistic and
instant. Cards carry a markdown description, due date, priority, labels, assignees, comments,
checklists, attachments, and custom fields. Human-readable **card keys** (`TRK-42`) resolve
anywhere. A **command palette** (⌘K) drives every action from the keyboard, and a global
**search** (Postgres full-text + structured filters, saved as personal or shared views) spans
every board you can read.

**Real-time, done right.** Optimistic UI renders the change immediately; the server reconciles
over the WebSocket with the authoritative state. **Fractional indexing** on card/list positions
means concurrent drags from different clients never fight over integer ranks, and every optimistic
mutation carries a **`client_mutation_id`** so a retry or a reconciliation is exact, never doubled.
Live presence shows who else is on the board.

**Agents as first-class principals.** An agent authenticates with a scoped API token and drives
the board over **REST or MCP** (same domain functions, proven equivalent by a contract test) or
reacts to **signed webhooks**. Cards are *assigned* to humans and **delegated** to agents. A
delegated (or @mentioned) agent works the card in an **agent session** — a glanceable state
machine (`queued → working ⇄ awaiting_input → done | error`) with an append-only event trail — and
when it hits a fork it calls **`ask_human`**, which routes the question to an accountable human and
renders one-click answer buttons. Agent work is *legible*, not a black box.

**MCP, consolidated.** The MCP server ships a tight, method-parameterized tool catalog
(`board_read` / `board_write` / `card_read` / `card_write` / `session_read` / `session_write` /
`my_work` / `automation_read` / `automation_write`) — 7 tools, ~44% less schema context than the
flat predecessor at strictly larger coverage. Connect over **Streamable HTTP** with a Bearer token,
via **OAuth 2.1** (claude.ai-style Connectors, no pre-shared token), or through a **stdio** proxy
for desktop agents. Compact markdown board/card **resources** let an agent read a board for ~15
tokens/card.

**Custom fields & attachments.** Per-board custom field definitions (typed, validated) attach
structured data to cards and are searchable. Cards take file attachments behind a pluggable storage
driver — zero-config **local** disk for self-hosting, or **presigned S3** (any S3-compatible
backend, MinIO included) for production, with server-enforced size, count, and content-type caps.

**Rules engine (agent-authorable).** Boards carry Butler-style **`trigger → filters → actions`**
rules that run automatically on board events. The twist: an agent can *draft* them from a plain
description over MCP. Rules run as a visibly non-human "Automation" actor, with depth-1 loop
guards, per-rule rate limits, and a ReDoS-proof safe-regex subset.

**GitHub / GitLab integration.** Link a repo to a board through card keys — a branch named
`trk-42-fix-login`, a PR titled `TRK-42: …`, or a `fixes TRK-42` magic word links the card. The
integration emits `vcs.*` events (`pr_opened`, `pr_merged`, …) onto the same event path the rules
engine watches, so a PR's lifecycle moves the card with no manual status updates. Each linked card
shows a state-colored PR chip.

**Admin console.** A settings UI covers the operational surface: mint scoped API tokens with a
live scope-implication preview and one-time secret reveal; manage webhooks with a per-delivery log,
manual redelivery, and dead-letter re-enable; and run the agent roster — capability toggles
(delegatable / mentionable), credential scopes, and recent activity per agent.

**Security model.** Human passwords are Argon2; agent tokens are SHA-256-hashed at rest, returned
in plaintext exactly once, and carry an explicit **scope set** (broad scopes imply narrower ones;
administrative scopes never implied). A **board leash** (`board:<id>`) confines a token to a single
board. Tokens can only mint strictly-weaker tokens. Authorization is enforced **once**, in the
domain layer — no adapter has a privileged path. Webhooks are HMAC-signed (v2, replay-protected).
Per-token rate limits are shared across REST and MCP.

## 60-second quickstart

Requires Docker.

```bash
git clone https://github.com/0-to-1-Labs/tracker.git
cd tracker
cp .env.example .env          # set SESSION_SECRET for anything non-local
docker compose up             # builds + runs web + api + mcp + postgres + redis
```

- **Web SPA** → <http://localhost:8080>
- **API** → <http://localhost:3000> (`/health`, `/openapi.json`, REST under `/api`, WS at `/ws`)
- **MCP** → <http://localhost:3001> (`/health`, Streamable HTTP at `POST /mcp`)

The api applies its Drizzle migrations on start, then serves. Seed a known login + demo board +
agent token (idempotent, re-runnable):

```bash
pnpm --filter @kanban/api db:seed
# → human login  demo@kanban.local / demo1234
#   "Demo Board" with To Do / In Progress / Done
#   a demo agent + Bearer token (printed once) for driving the same board via REST/MCP
```

Log in at <http://localhost:8080> with **`demo@kanban.local` / `demo1234`**, or register a fresh
account.

## Agent quickstart

An agent goes from zero to driving the board in three steps. Full guide:
**[docs/agents/quickstart.md](docs/agents/quickstart.md)**.

**1. Mint a scoped token** (as a logged-in human, or grab the one `db:seed` printed):

```bash
curl -s -X POST http://localhost:3000/api/tokens \
  -H 'content-type: application/json' -b "$SESSION_COOKIE" \
  -d '{ "label": "research-agent",
        "scopes": ["boards:read", "cards:write", "comments:write", "sessions:write"] }'
# → { "token": "kbt_…" }   ← shown once; copy it now
```

**2. Connect over MCP** (Streamable HTTP, Bearer token):

```
POST http://localhost:3001/mcp
Authorization: Bearer kbt_…
```

...or drive REST directly. Either way it's the same domain layer, same scopes.

**3. Create your first card:**

```bash
# MCP:  card_write { "method": "create", "params": { "listId": "…", "title": "Investigate flaky test" } }
# REST:
curl -H "Authorization: Bearer kbt_…" -H 'content-type: application/json' \
  -X POST http://localhost:3000/api/lists/$LIST_ID/cards \
  -d '{"title": "Investigate flaky test", "clientMutationId": "0f3c-…"}'
```

From there: pick up **delegated** work (`my_work { method: "delegated_to_me" }`), open a
**session**, post progress, `ask_human` when unsure, and `complete` with a summary. The MCP
handshake ships server instructions (actor model, delegation-vs-assignment, the `?since` poll
contract, idempotency) so you start legible with no extra reads. A runnable reference agent lives
in [`examples/session-agent/`](examples/session-agent).

## Architecture

One domain core; four thin adapters; a single Zod contract layer shared by all of them.

```mermaid
flowchart TB
  subgraph shared["packages/shared — the contract layer"]
    zod["Zod schemas = single source of truth<br/>every DTO / contract + the scope model"]
  end

  subgraph adapters["thin adapters (no privileged path)"]
    rest["REST routes"]
    ws["WebSocket gateway"]
    mcp["MCP tools"]
    hooks["outbound webhooks"]
  end

  subgraph core["apps/api/src/domain/* — the spine"]
    domain["domain functions: (ctx, validatedInput)<br/>authorize once · write Postgres · append Activity · publish"]
  end

  pg[("Postgres<br/>source of truth")]
  redis[("Redis pub/sub<br/>event fan-out")]

  zod -.imported by.-> rest & ws & mcp & hooks
  rest --> domain
  ws --> domain
  mcp --> domain
  hooks --> domain
  domain --> pg
  domain --> redis
  redis --> ws
  redis --> hooks
```

Every mutation authorizes once, writes Postgres, appends an `Activity` row (the audit trail +
webhook payload source + agent poll history), and publishes to Redis. Because Redis already fans
events out, scaling 1→N api instances needs no code change. IDs are UUID v7 minted in the app;
deletes are soft (an `archived` flag) so the `Activity` log is never rewritten.

**Monorepo (pnpm workspaces):**

| Package | What it is |
| --- | --- |
| `packages/shared` | Zod entity + DTO schemas and the scope model — the contract layer. |
| `packages/core` | The domain spine shared by the api and the standalone MCP server. |
| `apps/api` | Fastify: REST + WS + MCP + webhooks over the domain layer; Drizzle/Postgres; ioredis. |
| `apps/web` | React + Vite SPA: TanStack Query (optimistic mutations), Zustand, dnd-kit, Tailwind. |
| `packages/plugin-sdk` | Webhook verification + helpers for building reactive agents. |
| `e2e` | Playwright cross-client drag-and-drop E2E. |

**Stack:** TypeScript (strict) · Fastify · Drizzle ORM + Drizzle Kit · Zod · ioredis ·
`@modelcontextprotocol/sdk` · Argon2 / SHA-256 · React + Vite · TanStack Query · Zustand · dnd-kit ·
Tailwind (VS Code "Dark High Contrast" tokens) · Vitest · Playwright · Postgres · Redis · Docker
Compose.

## Documentation

- **[docs/agents/overview.md](docs/agents/overview.md)** — the agent guide: the actor model,
  delegation, sessions & `ask_human`, webhooks, automations, VCS linking.
- **[docs/agents/rest.md](docs/agents/rest.md)** — REST reference (canonical contract at `GET /openapi.json`).
- **[docs/agents/mcp.md](docs/agents/mcp.md)** — MCP transports, the consolidated tool catalog, and resources.
- **[Full documentation site](https://0-to-1-labs.github.io/tracker/)** — rendered guide, agent docs, and reference (GitHub Pages).
- **[CONTRIBUTING.md](CONTRIBUTING.md)** — dev setup, the test gate, and the contribution flow.

## Contributing

Contributions are welcome — see **[CONTRIBUTING.md](CONTRIBUTING.md)** for dev setup, the test
gate, the TDD convention, and the DCO sign-off requirement. Please also read our
[Code of Conduct](CODE_OF_CONDUCT.md). Security issues: see [SECURITY.md](SECURITY.md).

## License

Tracker is licensed under the **GNU Affero General Public License v3.0** — see [LICENSE](LICENSE).

---

<p align="center">
  <sub>A <strong>0&gt;1 Labs</strong> product · <a href="https://0to1.software">0to1.software</a></sub>
</p>