# Pursers

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/swisspra/Pursers  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/pursers

## Description
Coordinate AI-agent work on a local-first, owner-controlled MCP board

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "pursers": {
    "command": "npx",
    "args": ["-y","pursers"]
  }
}
```

## Documentation & README

<div align="center">

# Chat dies. The board doesn't.

**Not another MCP. The OS for AI agent work.**

One local board runs a whole agent fleet — any model, any MCP client.
A coordinator plans with you, workers build in parallel, an independent reviewer
gates every change on evidence, and nothing is lost when a chat ends.

[Quickstart](#quickstart) · [How a ticket moves](#how-a-ticket-moves) · [What's in the box](#whats-in-the-box) · [Docs](https://github.com/swisspra/Pursers/blob/HEAD/docs/GETTING-STARTED.md) · [pursers.app](https://pursers.app)

[![CI](https://img.shields.io/github/actions/workflow/status/swisspra/Pursers/ci.yml?branch=main&label=CI)](https://github.com/swisspra/Pursers/actions/workflows/ci.yml)
[![Latest release](https://img.shields.io/github/v/release/swisspra/Pursers?label=release)](https://github.com/swisspra/Pursers/releases)
[![PyPI](https://img.shields.io/pypi/v/pursers?label=pypi)](https://pypi.org/project/pursers/)
[![License: Apache 2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
[![MCP 2026-07-28](https://img.shields.io/badge/MCP-2026--07--28-6f42c1)](https://modelcontextprotocol.io/)

<sub>main: <code>5.0.5</code></sub>

</div>

> [!IMPORTANT]
> **Pursers was built by its own fleet.** From first commit to 5.0.0 on PyPI and
> the MCP Registry took **27 days**. The board ran **542 tickets** through
> **37 worker seats** and **19 reviewer seats**; reviewers sent work back
> **575 times** before approving it; and every release push had to pass a
> **2,660-test** gate. One human set the direction and made the calls.

| Before | After |
| --- | --- |
| Chat ends → work vanishes. Who owns what? Where's the proof? | The board keeps the ticket — claim, lease, evidence, review. Chat dies. The board doesn't. |

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="docs/media/ticket-flow-dark.svg">
    <img src="https://raw.githubusercontent.com/swisspra/Pursers/HEAD/docs/media/ticket-flow-light.svg" alt="A ticket moves from coordinator to board to worker, is rejected once by an independent reviewer, fixed, approved, and handed to you to merge" width="820">
  </picture>
</p>

## How a ticket moves

<details>
<summary><b>Step by step, as MCP tool calls</b></summary>

```mermaid
sequenceDiagram
    autonumber
    actor You
    participant C as Coordinator
    participant B as Board (Central)
    participant W as Worker
    participant R as Reviewer
    You->>C: intent
    C->>B: ticket_create
    B-->>W: offer — wakes the waiting seat
    W->>B: ticket_claim — lease starts
    Note over W: builds in its own checkout,<br/>renews the lease
    W->>B: ticket_submit — commit, files, test output
    B-->>R: review offer
    alt evidence holds
        R->>B: approve
    else evidence falls short
        R->>B: reject with fix instructions
        B-->>W: back to the same worker
    end
    B-->>You: approved work, ready to merge
```

</details>

**Recorded from the real product** — the Fleet dashboard following one ticket on a disposable board, from offer to independent approval:

<p align="center">
  <img src="https://raw.githubusercontent.com/swisspra/Pursers/HEAD/docs/media/ticket-flow.gif" alt="Fleet dashboard: a ticket moves Created, Offered, Claimed under a lease, Submitted, Reviewed, and closes approved" width="720">
</p>

| Role | Does |
| --- | --- |
| **Coordinator** | Talks to you, turns intent into tickets, amends them, answers the questions seats raise, keeps context on the board |
| **Worker** | Claims an offered ticket, builds under a renewable lease, submits exact evidence |
| **Reviewer** | A separate principal that approves or rejects on that evidence — never the seat that built it |
| **You** | Set intent, answer questions, merge approved work. The final call is yours |

### Why it sticks

1. **Worker ↛ Reviewer** — building and gating are separate principals. Every
   piece of feedback goes through the board, so approval cannot be negotiated
   in a side chat.
2. **Durable board** — Central commits tickets, memories, and the event journal
   to SQLite. The record outlives every chat, crash, and context compaction.
3. **Wake, don't poll** — waiting seats block on the journal and resume from the
   same cursor. An idle seat spends no model turns until there is work for it.

<p align="center">
  <img src="https://raw.githubusercontent.com/swisspra/Pursers/HEAD/docs/media/wake-dont-poll.gif" alt="A worker blocked in a2a_wait on the journal subscription is woken by a pushed ticket_offered event" width="560">
</p>

## From zero to production with a fleet

| Stage | What the board does |
| --- | --- |
| **Plan** | The coordinator splits a goal into bounded tickets with required evidence, forbidden actions, tier, and skills. |
| **Build in parallel** | Each ticket is offered to one eligible seat. Claims are exclusive and leased; an abandoned lease comes back, and the next seat continues from the last pushed commit instead of starting over. |
| **Prove** | A worker cannot close its own work. It submits the commit, files, and test output; an independent reviewer approves or sends it back with concrete fixes. |
| **Ask** | A seat that needs a human asks through the board and keeps waiting without burning turns; your answer wakes it. |
| **Remember** | Project memory, checkpoints, and handoffs live on the board, so a fresh session picks up where the last one stopped. |
| **Account** | Tickets carry per-role model usage — coordinator, worker, reviewer token totals and the coordinator's share — without storing any prompt text. |
| **Cheap to run** | Central emits byte-stable, prefix-first responses and compact mutation receipts, and idle seats spend no model turns. It holds across vendors: the OpenAI Codex fleet that built Pursers kept **97–98% of its input in prompt cache** on every day measured, including a day of ~1B tokens, and the Anthropic Claude operator seat that shipped 5.0.0 ran at **99%**. [Design](https://github.com/swisspra/Pursers/blob/HEAD/docs/cache-friendly-prose.md) · [numbers](https://github.com/swisspra/Pursers/blob/HEAD/docs/performance/cache-efficiency.md) |
| **Watch** | The Fleet dashboard shows the ticket funnel, live seats, claims, review pressure, and every project board on one screen. |

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="docs/media/cache-hit-dark.svg">
    <img src="https://raw.githubusercontent.com/swisspra/Pursers/HEAD/docs/media/cache-hit-light.svg" alt="Prompt-cache hit rate while the fleet built Pursers: OpenAI Codex fleet 97.6%, 97.5% and 97.2% on three days, Anthropic Claude operator seat 98.8%; only 1 to 3% of input was paid at full price" width="820">
  </picture>
</p>

Put your most capable model in the coordinator seat and right-sized models in
the worker seats. Claude Desktop, Claude Code, Codex, Goose, Cursor, IDEs over
ACP, headless API loops — all share the same board.

## Quickstart

```bash
python3 -m venv .venv && . .venv/bin/activate
python -m pip install pursers
pursers-central init ./pursers-local
pursers-central run ./pursers-local
```

Connect any Streamable HTTP MCP client to `http://127.0.0.1:8766/mcp` — use
`admin.jwt` first to create the board, then `worker.jwt` for a worker seat.
`init` prints credential paths, never values. The packaged Central
[quickstart](https://github.com/swisspra/Pursers/blob/HEAD/packages/central/README.md#quickstart) explains every generated file.
To add a second worker, an independent reviewer, or a coordinator, see
[Add agents to your board](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/adding-agents.md).

> [!TIP]
> **More than one machine?** Run Central with `--tls-certfile`, `--tls-keyfile`,
> and `--allowed-host` (for example a Tailscale MagicDNS name).
> **Claude Desktop on macOS?** `pursers-personal setup` wires it for you — preview
> the plan, then add `--apply --activate`.

> [!NOTE]
> Keep Pursers in its own virtual environment. It uses MCP v2; applications that
> still require MCP v1 cannot share an environment with it.

### Use Pursers from Zed

Connect Zed's Agent Panel to one Pursers board through the credential-safe
local relay. Follow the [first-ticket walkthrough](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/zed-first-ticket.md)
to create and follow one ticket, including the exact point where an operated
worker/reviewer fleet becomes necessary, or use the [Zed reference
guide](docs/guides/zed.md) for installation, settings, all five board commands,
the optional ACP thread, and troubleshooting.

## What's in the box

Everything below is on `main` and covered by the test gate. **Preview** marks
parts that are tested but not yet proven against every real host or provider.

| Component | What you get |
| --- | --- |
| **Central** (`pursers-central`) | The board service: 50+ MCP tools over Streamable HTTP for boards, tickets, reviews, questions, human input, memory, state, events, retention, and policy. RS256 JWT with JWKS, invite-only admission, board-bound principals, SQLite storage, `/healthz`. |
| **Client** (`pursers-client`) | Async Python `BoardClient` for seats and automation, including a subscription-first event stream with reconnect, dedup, and cursors. |
| **Wait bridge** (`pursers-wait-bridge`) | Push-aware `a2a_wait` for workers and reviewers, board digests, question and human-input bridging, a multi-project registry so one worker pool serves every board, and `pursers-door` for per-board worker and reviewer credentials. |
| **Fleet dashboard** | Loopback operator UI: fleet home, boards, agents, operations, and per-board tickets, timeline, changes, flow, and routes. Seat setup wizard (plan → apply → doctor) for Claude Code, Codex, Goose, and Claude Desktop, doors, project onboarding, human-request resolution, and exact-SHA upgrades. |
| **Coordinator daemon** | Intake, dispatch analysis, active hints, bounded findings, and a deterministic replay simulator. |
| **Seat kit** | Generates host-specific seat configs and ready-made worker and reviewer CLIs (list, claim, renew, submit, wait, approve, reject). |
| **Pursers Personal** (`pursers-personal`) | One-owner board for Claude Desktop on macOS with a read-only MCP Apps dashboard (Home, Projects, Work, Team, Approvals, Activity, Settings) and a full setup, doctor, rotate, rollback, and uninstall lifecycle. |
| **Personal import** (`pursers-personal-import`) | One-way, reviewable import from On Board v4 with retry and rollback. |
| **ACP agent** (`pursers-acp`) | Board assistant for ACP IDE hosts such as Zed: your tickets and offers, board status, permission-gated create and annotate, and live watch. *Preview.* |
| **Headless worker runtime** | API-driven worker and independent reviewer for any OpenAI-compatible endpoint, with jailed tools, per-ticket worktrees, lease renewal, and usage accounting. *Preview.* |
| **Board Butler** | Refreshes coordinator findings and drafts evidence-backed responses to coordinator questions. A separately authorized active mode can perform two narrow safety actions. *Preview.* |
| **Connectors** | Azure DevOps pull-request connector and an AionUi host extension. *Preview.* |
| **Board move** | Export and import a board between Central instances. |

### What Board Butler does

Board Butler is a coordinator-side observer with two narrow safety actions; it
is not an autonomous fleet operator.

- **Watches:** on each bounded refresh it runs the real coordinator derivation
  for every active board in `project_registry` and refreshes that board's
  findings. Inactive registry projects are ignored
  (`CentralBackend.refresh_registry_findings` in the
  [implementation](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/board_butler.py), exercised across two
  boards in the [tests](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/tests/test_board_butler.py)).
- **Drafts and escalates:** it listens for coordinator questions through the
  journal push stream, checks current product or repository evidence, and
  stores a rate-limited draft, durable hold, and evaluation record. Approval or
  decision questions, scope changes, gate waivers, release actions, membership
  or registry changes, and incomplete evidence stay with a human. The butler
  records the reason for escalation; it does not send the answer
  (`process_question` and `classify_question` in the
  [implementation](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/board_butler.py), with escalation cases
  and the absent answer path enforced by the
  [tests](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/tests/test_board_butler.py)).
- **Shadow versus active:** shadow is the default and performs no ticket
  action. Active mode additionally requires a separate owned mode-`0600`
  authorization, at least one `--act-on-board`, an active registry board, an
  enabled action class, and a durable hold that expires without a veto. Only
  then may it park an open ticket after repeated `no_live_candidates` cycles
  when no live `can_work=true` seat exists, or annotate refusal of a proposed
  escalation target that is missing or cannot work. It neither cancels the
  ticket nor assigns the target (`plan_mechanical_actions` and
  `refresh_registry_findings` in the
  [implementation](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/board_butler.py), covered by the
  [mechanical-action tests](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/tests/test_board_butler.py)).
- **Never does:** it does not merge or push `main`, tag or publish a release,
  change membership or the project registry, claim/assign/submit work, or
  answer a blocked seat's question. Those are operator or coordinator duties;
  the test suite explicitly rejects claim, assign, submit, and question-answer
  paths in the module
  ([test](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/tests/test_board_butler.py)).

> **Stale-log finding:** the reported stale `butler.out.log` is a legacy-file
> mismatch, not a liveness defect. The current launch job and checked-in
> [service template](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/com.pursers.board-butler.plist.template)
> send both output streams to `board-butler.log`; on 2026-09-22 that configured
> log and `runtime.json` advanced together while `butler.out.log` did not.
> Check the installed job's `StandardOutPath` before treating an old filename
> as service status. `runtime.json` reports PID, mode, start time, and last
> activity; Fleet also verifies the pidfile lock and live process, as described
> in the [Board Butler runbook](https://github.com/swisspra/Pursers/blob/HEAD/tools/board-butler/README.md).

#### Board Butler behaviour checklist

1. **Stale question reconciliation:** in shadow and active modes, an open
   coordinator question explicitly named by a later decision is reported for
   coordinator reconciliation. A chronological but unlinked decision reports
   its missing correlation instead of being guessed as the answer. The butler
   does not answer or close the question.
2. **Held-decision travel:** in both modes, a binding decision followed by a
   later work offer or broadcast is surfaced before another seat re-derives
   the gate. The butler does not claim, assign, or release the work.
3. **Standing-rule recurrence:** in both modes, questions from multiple seats
   that reuse the same exact board-visible identifier are linked to the
   standing decision. The butler does not infer a match from free-text
   similarity or enforce the decision on a seat.
4. **Decision-scope drift:** in both modes, a decision that directs a file
   change outside the ticket's declared related-file boundary is reported for
   preflight reconciliation. The butler does not edit the ticket scope or the
   repository.
5. **Bounded and fail-closed:** critical coordinator alerts rank first; current
   observations then displace only older non-critical rows inside the 50-row,
   4,800-character limit. Incomplete question or ticket projections produce a
   coverage warning, never a clean finding.
6. **No added authority:** all observers are read-only in shadow and active
   modes. Active mode still adds only the two separately authorized safety
   actions documented above. The butler never deletes files or board data,
   dispatches cleanup, merges or pushes `main`, tags or publishes, changes
   membership or the registry, or acts for the operator.

### Packages

| Package | What it is |
| --- | --- |
| `pursers==5.0.5` | Installs Central, the client, Personal, and the importer |
| `pursers-central==0.1.3` | The board service |
| `pursers-client==0.1.4` | Async Python client |
| `pursers-personal==5.0.5` | One-owner board and MCP App dashboard |
| `pursers-personal-import==5.0.0` | Importer from On Board v4 |
| `pursers-wait-bridge==0.1.2` | Wait bridge and door tooling for seats |
| `pursers-acp==0.1.3` | ACP board assistant for IDEs |

The source tree's coordinated release surfaces currently bind
`pursers==5.0.5`, `pursers-personal==5.0.5`,
`pursers-personal-import==5.0.0`, `pursers-central==0.1.3`,
`pursers-client==0.1.4`, `pursers-wait-bridge==0.1.2`, and
`pursers-acp==0.1.3`.

## Architecture

```mermaid
flowchart LR
    subgraph Seats
      C[Coordinator]
      W1[Worker]
      W2[Worker]
      R[Reviewer]
    end
    C & W1 & W2 & R -- "MCP + JWT" --> Central[("Central<br/>SQLite board + journal")]
    Bridge[Wait bridge] -- "subscriptions/listen" --> Central
    W1 & W2 & R -. "block until offered" .-> Bridge
    Central --> Dash[Fleet dashboard]
    Central --> Personal[Personal MCP App]
    W1 & W2 -- "commits" --> Git[(Git)]
    R -- "verifies" --> Git
```

Central is the source of truth. Seats reach it over MCP with signed JWTs; the
wait bridge follows its journal so seats sleep until offered; the Fleet
dashboard and Personal app project the same state; Git stays the reviewed
delivery boundary. Read [Architecture](https://github.com/swisspra/Pursers/blob/HEAD/docs/ARCHITECTURE.md) for the full
component, trust, transport, and lifecycle diagrams.

<details>
<summary><b>Screenshots</b></summary>

[![Fleet overview](https://github.com/swisspra/Pursers/blob/HEAD/docs/showcase/01-fleet-overview.png)](docs/showcase/01-fleet-overview.png)

Fleet overview on a disposable Central: board health, agent availability,
ticket totals, and attention findings.

[![Personal Today view](https://github.com/swisspra/Pursers/blob/HEAD/docs/showcase/02-personal-today.png)](docs/showcase/02-personal-today.png)

Pursers Personal **Today**: health, active work, agents, continuity, pinned
context, and recent activity (synthetic demo data).

[![Offer and claim](https://github.com/swisspra/Pursers/blob/HEAD/docs/showcase/06-aionui-offer-claim.png)](docs/showcase/06-aionui-offer-claim.png)

Live offers from a disposable Central and an exact-identity claim.

[See the full verified showcase.](https://github.com/swisspra/Pursers/blob/HEAD/docs/showcase/README.md)

</details>

## Documentation

- [Getting Started](https://github.com/swisspra/Pursers/blob/HEAD/docs/GETTING-STARTED.md)
- [Add agents to your board](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/adding-agents.md) — a second worker, an independent reviewer, a coordinator
- [Connect your MCP client](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/connecting-clients.md) — Claude Code, Codex, Cursor, Goose, Claude Desktop, Zed, API loops
- [Finish your first ticket in Zed](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/zed-first-ticket.md) — a narrative walkthrough and recording shot list
- [Use Pursers from Zed](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/zed.md) — install, configure, board commands, ACP thread, troubleshooting
- [Run a multi-agent fleet](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/running-a-fleet.md) — coordinator, workers, reviewer, end to end
- [Operate Central](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/operating-central.md) — run as a service, backup and restore, upgrade, logs, retention
- [Fleet dashboard and Board Butler](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/fleet-dashboard.md) — install, every page, seat wizard, doors
- [Troubleshooting and FAQ](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/troubleshooting.md) — real error messages mapped to fixes
- Reference: [MCP tools](https://github.com/swisspra/Pursers/blob/HEAD/docs/reference/mcp-tools.md) · [CLI](https://github.com/swisspra/Pursers/blob/HEAD/docs/reference/cli.md) · [environment variables](https://github.com/swisspra/Pursers/blob/HEAD/docs/reference/environment.md) — generated from code
- [Architecture](https://github.com/swisspra/Pursers/blob/HEAD/docs/ARCHITECTURE.md)
- [Security guide](https://github.com/swisspra/Pursers/blob/HEAD/docs/guides/security.md) — trust model, credentials, remote access, leak response
- [Rotating the issuer key without downtime](https://github.com/swisspra/Pursers/blob/HEAD/docs/operations/issuer-key-rotation.md)
- [Comparison with other agent frameworks](https://github.com/swisspra/Pursers/blob/HEAD/docs/COMPARISON.md)
- [Contributing](https://github.com/swisspra/Pursers/blob/HEAD/CONTRIBUTING.md) · [Security policy](https://github.com/swisspra/Pursers/blob/HEAD/SECURITY.md) · [Changelog](https://github.com/swisspra/Pursers/blob/HEAD/CHANGELOG.md)

## Current limitations

Central binds plain HTTP on loopback by default. TLS is operator-supplied for
remote use, together with an allowed host. Storage is SQLite, and boards admit
agents by invite. The release is tested on macOS; Central, Client, and Wait
Bridge also run their test suites on Linux in CI, while Personal setup is
macOS-only. Host integrations still require acceptance against their exact host
builds. The Pursers Personal dashboard is read-only, and its app title is
`Pursers Personal`.

Pursers is the successor to On Board v4 (`onboard-memory-mcp` 4.0.4). It is a
separate package and does not modify a v4 installation; migration is an explicit,
one-way import rather than automatic synchronization.

## License

[Apache License 2.0](https://github.com/swisspra/Pursers/blob/HEAD/LICENSE)

