SpecStrand Docs

MCP setup

The same work loop the spec CLI drives is available as an MCP server, so an MCP-capable agent (Claude Code, Codex, OpenCode, Cursor, Claude Desktop) can claim, report, and complete tasks with structured tools instead of shell commands. The server is mounted at https://api.specstrand.com/mcp.

1. Mint a token

The MCP server authenticates with a personal access token. Generate a quickstart token in the web app under Settings → MCP. It is shown once and is task-scoped (it can claim and work your tasks; it cannot push repos or manage other tokens). Copy it before you leave the page.

2. Connect your agent

Claude Code: register the server once, with your token in place of the placeholder:

claude mcp add --transport http --scope user specstrand \
  https://api.specstrand.com/mcp \
  --header "Authorization: Bearer <your-token>"

Codex: add this to ~/.codex/config.toml:

[mcp_servers.specstrand]
url = "https://api.specstrand.com/mcp"
bearer_token_env_var = "SPEC_TOKEN"

Codex reads the token from the SPEC_TOKEN variable in the shell that launches it, the same variable the spec CLI uses.

Fallback: to hold the token in the file instead, replace that line with http_headers = { Authorization = "Bearer <your-token>" }.

OpenCode: add this to opencode.json in your project root, or to ~/.config/opencode/opencode.json for every project:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "specstrand": {
      "type": "remote",
      "url": "https://api.specstrand.com/mcp",
      "oauth": false,
      "headers": { "Authorization": "Bearer {env:SPEC_TOKEN}" }
    }
  }
}

OpenCode replaces {env:SPEC_TOKEN} with the SPEC_TOKEN variable from the shell that launches it, so the token stays out of the file. "oauth": false stops OpenCode from trying an OAuth sign-in, because the token already authenticates.

Fallback: to hold the token in the file instead, write "Authorization": "Bearer <your-token>" in headers.

Cursor or Claude Desktop: add this to your client's MCP config (e.g. mcp.json):

{
  "mcpServers": {
    "specstrand": {
      "url": "https://api.specstrand.com/mcp",
      "headers": { "Authorization": "Bearer <your-token>" }
    }
  }
}

Protocol versions: the server speaks MCP 2026-07-28 (stateless requests with server/discover) and 2025-11-25 (the initialize handshake; 2025-06-18 clients also work) on the same URL. Your client picks the version; there is nothing to configure.

Then give your agent its first job. A new account comes with a Getting started graph; paste this into the agent once it is connected:

Work the Getting started graph in SpecStrand: call project.work with project getting-started and follow each task until there is nothing to claim.

3. Drive the loop

Graphs come first. An agent starts multi-plan work with graph.list and graph.get, calls graph.create when no graph fits, and creates plans into it with plan.create and its graph argument (graph.add moves an existing plan in). A plan created or updated outside every graph returns a plan_not_in_graph warning with the exact follow-up call.

On connect, the server sends an instruction that tells the agent to route multi-step, project-like work through SpecStrand. A 2025-11-25 client receives it in the initialize result and a 2026-07-28 client in the server/discover result; the text is the same. This is that instruction, verbatim, so what you read here is exactly what your agent was told:

By default, route ALL multi-step or project-like work through SpecStrand:

  • Start with a graph. A graph holds related plans and their drain order. Before creating plans, call graph.list, then graph.get on the one that fits; if none fits, graph.create one first. Create plans into it (plan.create with graph) or add existing plans with graph.add. A plan outside every graph comes back with a plan_not_in_graph warning: fix it.
  • Capture the work. A concrete unit of work becomes a plan with tasks; add tasks as the shape firms up (task.create, task.decompose).
  • Before choosing work, graph.get shows which member plans are ready vs blocked and why.
  • Execute with the self-driving loop: call project.work to claim the next ready task, do it, task.report progress, then task.complete (or task.fail), and call project.work AGAIN. Repeat until you get "no claimable task", which means the project is drained.
  • Plans drain in order; a plan marked "next up" (plan.queue) is claimed first.

Gates: a gate is a graph decision node that blocks its downstream plans until answered. Create one with plan.create + gate_prompt (set gate_bypass: true only when the user delegated that decision to you). Write any checklist in a gate prompt as GFM task-list lines (- [ ] item) so it renders as checkboxes in the UI. Gates are answered by the human in the UI (tokens cannot call the answer endpoint); a bypass-on gate is decided by claiming and completing its decision task. A drain pausing at a waiting gate means a human decision is pending: stop and say so.

Exempt: you do NOT need SpecStrand for answering a general question, a quick lookup, or a small one-off the user asked for directly. When work spans multiple steps or the user will want it tracked and resumable, capture it here first.

4. Make agents use it from the first message

The initialize instruction only surfaces after the agent's first tool call. To drive from the very first message and across clients, paste a short rule into your project's AGENTS.md or CLAUDE.md:

## SpecStrand

This project is connected to SpecStrand over MCP. Route multi-step or
project-like work through it by default:

- Start with a graph: graph.list / graph.get first; graph.create when none
  fits; then create plans into it (plan.create with graph) or graph.add them.
- Capture first: a concrete unit of work -> a plan + tasks (plan.create,
  task.create); a rough thought -> idea.create.
- Drive the loop: project.work claims the next ready task; do it, then
  task.report, task.complete (or task.fail), and call project.work again
  until "no claimable task".
- Gates: a gate is a graph decision node that blocks its downstream plans
  until a human answers it. A drain that pauses at a waiting gate means a
  decision is pending; stop and say so.
- Exempt: general questions, quick lookups, and small one-offs.

Capture freely; confirm before starting an autonomous multi-task drain.

Prefer a shell-driven agent? The spec CLI exposes the same loop over REST; see the getting-started guide.