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_updatewith an optional field set, nottask_set_titleplustask_set_statusplustask_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.
| Tool | Arguments | Returns |
|---|---|---|
whoami | none | the calling agent, its capabilities, its tool allowlist, its open leases |
tasks_search | status, label, project, assignee, free text, cursor, limit | one cursor-paginated page of tasks in the compact projection |
tasks_get | task id or slug | one task with brief, acceptance criteria, comments, context refs, legal next transitions |
tasks_create | task fields, idempotency key (required) | the created task; a replay returns the original result |
tasks_update | task id, partial field set including a status transition, version to match | the updated task, or an error naming the legal transitions |
tasks_claim | agent id, capability filter | a lease with a TTL plus the full working context |
runs_heartbeat | run id, progress percentage, one-line status | the renewed lease |
runs_finish | run id, outcome, exit reason, token and cost usage, PR url | the closed run |
comments_post | task id, body, blocking flag | the posted comment |
artifacts_attach | task id or run id, kind, the file or link | the stored artifact, content-addressed so re-uploads dedupe |
events_stream | since event id | the events recorded after that id |
documents_search | query | semantic and keyword matches over the knowledge base |
documents_upsert | body, tags, supersedes | the written decision, runbook or ADR |
approvals_request | question, options | the pending approval; the task sits in blocked until it resolves |
agents_list, machines_list | filters | agents 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:
tasks_claimto get the lease and the working context.runs_heartbeatat least everyleaseTtl / 3, with a one-line human-readable status.comments_poston any decision a human would want to see, and on every blocker.artifacts_attachfor the diff, the PR link, and any screenshot proving the work.runs_finishwith 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.documents_upsertfor 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.