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, thengraph.geton the one that fits; if none fits,graph.createone first. Create plans into it (plan.createwithgraph) or add existing plans withgraph.add. A plan outside every graph comes back with aplan_not_in_graphwarning: 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.getshows which member plans are ready vs blocked and why.- Execute with the self-driving loop: call
project.workto claim the next ready task, do it,task.reportprogress, thentask.complete(ortask.fail), and callproject.workAGAIN. 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(setgate_bypass: trueonly 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.