Introduction

OverviewArchitectureAgent Experience

The product

This repository

Structure

Usage

Other

Interfaces

MCP Server

The planned MCP tool surface a remote Claude Code or Codex session uses to claim work and write results back

Not built yet. There is no MCP server in this repository and no endpoint to connect to. This page describes the intended shape, taken from spec/02-agent-interfaces.md.

Where it sits

Four doors open onto one service layer. Validation, the task state machine and event writes live in that layer, not in any door.

MCP server  ┐
REST API    │
GraphQL API ├──▶ service layer (validation, state machine, events) ──▶ D1
Browser UI  ┘

The spec calls MCP the primary door, meaning it is the door a remote Claude Code or Codex session reaches for first. That is a statement about routing, not about capability. No interface may hold business logic the others lack, and no interface may bypass the state machine.

Planned location: apps/api, as a streamable-HTTP MCP endpoint at /mcp.

Tool design rules

  • Few wide tools beat many narrow ones. Tool sprawl burns context and degrades selection accuracy. The target is around 15 tools, not 60. One tasks_update with an optional field set, not task_set_title plus task_set_status plus task_set_priority.
  • Every tool description states when to use it, when not to use it, and gives one worked example. The description is the only documentation the model gets.
  • Annotations are honest: readOnlyHint, destructiveHint, idempotentHint. Clients gate confirmation prompts on these.
  • Inputs use strict JSON Schema, with enums for anything enumerable, so the model cannot invent a status string.
  • Tools return structured typed content plus a short human-readable summary line.

Tool surface

Tool names come from the spec. Argument names are not frozen until the strict JSON Schema exists; the schema is the authority once the server is built.

ToolArgumentsReturns
whoaminonethe calling agent, its capabilities, its tool allowlist, its open leases
tasks_searchstatus, label, project, assignee, free text, cursor, limitone cursor-paginated page of tasks in the compact projection
tasks_gettask id or slugone task with brief, acceptance criteria, comments, context refs, legal next transitions
tasks_createtask fields, idempotency key (required)the created task; a replay returns the original result
tasks_updatetask id, partial field set including a status transition, version to matchthe updated task, or an error naming the legal transitions
tasks_claimagent id, capability filtera lease with a TTL plus the full working context
runs_heartbeatrun id, progress percentage, one-line statusthe renewed lease
runs_finishrun id, outcome, exit reason, token and cost usage, PR urlthe closed run
comments_posttask id, body, blocking flagthe posted comment
artifacts_attachtask id or run id, kind, the file or linkthe stored artifact, content-addressed so re-uploads dedupe
events_streamsince event idthe events recorded after that id
documents_searchquerysemantic and keyword matches over the knowledge base
documents_upsertbody, tags, supersedesthe written decision, runbook or ADR
approvals_requestquestion, optionsthe pending approval; the task sits in blocked until it resolves
agents_list, machines_listfiltersagents and machines, for routing and handoff

A comments_post carrying blocking: true that nobody answers moves the task to blocked.

Errors are teaching material

An error returns the reason, the legal alternatives, and the exact follow-up call. An agent must never have to string-match a message.

status 'merged' is not valid. Legal: in_review, ready_to_merge, blocked.
Current status is in_progress.

Resources and prompts

  • Resources expose the specs themselves: spec://domain-model, spec://state-machine, spec://conventions, spec://graphql-schema. An agent reads the rules the server enforces instead of guessing them.
  • Prompts ship the canonical workflows: start-work, hand-off, report-blocked, close-out. A new agent runs a prompt rather than reinventing the sequence.

Authentication

The endpoint authenticates with an AgentToken. Tokens are scoped per agent and per machine, hashed at rest, and revocable one at a time without touching the others. Every request is logged with the agent id.

The toolAllowlist on the Agent record is enforced server-side. The MCP server refuses a tool the agent lacks. It does not merely hide it. Destructive and outward-facing actions require an Approval, and the approval is recorded.

Rate limits are per token. A rejection returns the remaining budget.

Write-back contract

The product depends on remote runners reporting honestly. The minimum a runner sends:

  1. tasks_claim to get the lease and the working context.
  2. runs_heartbeat at least every leaseTtl / 3, with a one-line human-readable status.
  3. comments_post on any decision a human would want to see, and on every blocker.
  4. artifacts_attach for the diff, the PR link, and any screenshot proving the work.
  5. runs_finish with the true outcome. A failed run reported as failed is worth more than a green lie, and the acceptance criteria checkboxes must reflect what was actually verified.
  6. documents_upsert for anything the next agent would otherwise have to rediscover.

A runner that stops heartbeating is assumed dead, not done. The lease expires, the Run is marked abandoned, and the Task returns to todo.

Resuming after a crash

An agent must be able to restart and continue with no human help: whoami, then read its open leases, then tasks_get, then events_stream with since set to the last event id it saw, then back to work.