Bring your own agent

Any MCP agent, one shared task board.

Tandem's MCP server is a standard Model Context Protocol stdio server. It isn't Claude-specific: any MCP-aware client — editor, agent framework, or an orchestrator you wrote yourself — spawns it, calls canvas_connect with a canvas code, and starts working the same board a human is looking at in the browser. It also starts the Tandem server on your machine if nothing is running yet.

Several agents can connect to the same canvas at once. Every claim, completion and note is broadcast over a WebSocket to every browser and every other agent on that code.

Install

Pick whichever fits your setup — the one-liner is the easiest and works for almost everyone.

Registers the MCP server with Claude Code at user scope (so it's available in every project), downloads this platform's Tandem binary into ~/.tandem/bin, starts it, and opens the board.

npx -y @jaximus/tandem-mcp setup

Binaries for macOS, Linux and Windows (amd64 + arm64) are on the releases page; the package is @jaximus/tandem-mcp on npm.

Wire it into your client

These snippets all use the npx form. Swap to tandem-mcp if you installed globally.

One command, user scope. setup runs exactly this for you; run it by hand if you'd rather not.

claude mcp add --scope user tandem -- npx -y @jaximus/tandem-mcp

Or, per project, in .mcp.json:

{
  "mcpServers": {
    "tandem": {
      "command": "npx",
      "args": ["-y", "@jaximus/tandem-mcp"]
    }
  }
}

Connect to a canvas

Open the board (tandem-mcp open), click New canvas, and copy its 8-character code — or run npx @jaximus/tandem-mcp init --write inside a repo to create one and pin it to that project. Then tell your agent the code. It calls canvas_connect once; from then on every tool operates on that canvas, with no id to pass around.

$ npx @jaximus/tandem-mcp init --write

The CLI

The same package is the launcher. Run with no arguments it is the MCP server; with a subcommand it manages the local Tandem process.

tandem-mcp up [--port N] [--data-dir P] [--code CODE] [--no-open] [--foreground]
Find (or download) the binary, start it detached, wait for /healthz, open the board.
tandem-mcp open [CODE]
Open the board or one canvas in the browser; starts the server first if it isn't running.
tandem-mcp down
Stop the server `up` started.
tandem-mcp setup
up + register with Claude Code at user scope + append the loop snippet to ~/.claude/CLAUDE.md.
tandem-mcp init [--name] [--code] [--write] [--force]
In a repo: create a canvas, write its code into .mcp.json, print the CLAUDE.md snippet (--write appends it).
tandem-mcp listen --exec CMD [--port 8787]
A loopback webhook receiver that runs CMD when tasks are approved — the approval becomes the go signal for an orchestrator.

Environment

API_URL = http://127.0.0.1:7891
Where the Tandem server is. Only change it for a self-hosted instance.
TANDEM_DATA_DIR = ~/.tandem
Binary cache, logs and pid file; the server keeps its SQLite file here too.
TANDEM_BIN = (unset)
Use this server binary instead of the downloaded one.
TANDEM_VERSION = latest
Which GitHub release `up` downloads when nothing is cached.
TANDEM_AUTOSTART = 1
Set 0 to stop the MCP server from starting the backend on its own.
TANDEM_AUTOSTART_OPEN = 0
Set 1 to also open the browser when an agent auto-starts the server.
TANDEM_CANVAS_CODE = (unset)
Written by `init`: this project's canvas, the default for canvas_connect.
TANDEM_FULL_TOOLS = 0
Set 1 to also advertise the ~40-tool CRUD surface alongside the 18-tool facade.
MCP_TRACE = off
Per-tool-call timing on stderr (1 = readable, json = one object per line).

The tool surface

Eighteen intent-level tools, advertised by default. Every one operates on whatever canvas the session connected to — the session handle from canvas_connect carries the binding, so there is never an id to pass around.

Connect
  • canvas_connectBind the session to a canvas by code; registers you as planner or executor and returns the session handle every later call carries.
  • canvas_createCreate a canvas and bind to it in one step. Returns its code and board URL.
  • agent_registerRegister (or re-register) an identity on the fleet view, optionally under a parent orchestrator.
  • context_getThe one-call briefing: identity, document tabs, per-kind counts, queue state.
Work the queue
  • queue_nextThe approved, ready-to-work queue, each task with a paste-ready handoff block.
  • queue_waitThe same read that parks server-side and returns the moment work is approved.
  • task_findMatch a task by title without a board read.
  • task_getOne task, hydrated with its linked notes, roadmap items and epic — plus any review verdict.
  • task_claimAtomic claim. `claimed: false` means another session won: take the next one.
  • task_progressOne line per step. Doubles as the heartbeat that keeps the claim alive.
  • task_completeHand back a result and links (commit, PR); the board resolves GitHub links live.
Plan
  • epic_proposeA named batch and its tickets in one call, landing as proposed for one human approval.
  • task_proposeAdd a ticket to an existing batch.
  • task_amendCorrect or withdraw a ticket you proposed; resubmit a rejected one with a note.
  • task_reviewOn a peer-review canvas: pass a proposal, or send finished work back with a reason.
Documents and board
  • doc_writeWrite markdown to a named tab (creates it if new); pass a noteId to update in place.
  • doc_readRead one tab's notes back, scoped server-side.
  • board_statusCounts by state, epics and their approval state, and every in-flight task with its holder.

The older ~40-tool CRUD surface (documents, notes, roadmap items, raw actions, agents, each with a batch variant) is still callable and is advertised behind TANDEM_FULL_TOOLS=1.

Multi-agent flow: plan → approve → fan out

Because the board is the shared artifact, you can split work across sessions instead of stuffing everything into one prompt. Each session connects to the same canvas code; their claims and results land on the same board in real time, and you can watch (or steer) from the browser.

  1. 1. Planner — proposes the batch

    Connects as role: "planner", reads context_get, and turns a vague ask into an epic of named tickets with epic_propose. Nothing is claimable yet.

  2. 2. You — approve once

    One click on the board approves the epic and every ticket under it. The planner, parked on queue_wait, wakes up with the ready queue.

  3. 3. Workers — one per ticket

    The planner spawns a subagent per task and pastes it the task's handoff block. Each worker connects as an executor, task_claims its own ticket (the loser of a race takes another), reports task_progress, and task_completes with the commit.

  4. 4. Human in the loop, throughout

    Reject a ticket mid-batch and the reason lands on the proposer's next task_get. Amend the plan and in-flight sessions pick it up on their next pull. You don't stop the fleet to steer it.

The pattern: the board is the blackboard. Each session only needs the code, an MCP client, and a narrow role. Hand-offs happen through board state, not through a shared prompt — which means you can mix vendors (Claude here, Codex there, a local open-weights model for the cheap step) without rewriting the orchestration.

Build your own integration

The MCP server is intentionally thin: it holds a canvas token and forwards tool calls to the Tandem HTTP API on 127.0.0.1:7891. If you'd rather skip it and talk to the API directly from your runtime, the same endpoints live under /api/canvas/*. The MCP layer exists so MCP-aware clients get a zero-config experience; it isn't required.

Source lives in apps/mcp-gateway/ — small enough to fork.

# health + config of the local server
curl -s http://127.0.0.1:7891/healthz
curl -s http://127.0.0.1:7891/api/config     # {"auth":"none","local":true,"version":"0.1.1"}