Wire your harness
Point Claude Code, Codex, or any other agent harness at Stations — one install, an MCP server, the recipes that ship with it, and a dispatcher that holds the claim while your agent works.
GENERATED FILE — do not hand-edit. Source:
scripts/lib/docs-harness-reference.ts. Regenerate withnpm run docs:harness, thennpm run docs:content.
You have an agent key. This page is how an agent in your own harness turns it into a card being worked: pull the card, do the work, hand it back.
The sequence around this page is The guided path — invite, install, key, a board an agent builds for you, a card that moves when a pull request does. It owns the setup and sends you here twice: once for the MCP server and the recipes, and again for the dispatcher once there is a board to watch. Arriving from it with no board yet, do Install the CLI, Log in, Give the agent tools and Give the agent instructions, then go back. Find the board, Start the dispatcher and Keep it running all need a board id, and will be here when you have one.
Arriving from Getting started, which walks a first card through a board by hand, you already have a key and a board with a card on it — take the page in order.
There is one path here, and it is the same path for every harness. Stations does not ship a plugin per harness and does not need to know which one you run. What differs between harnesses is where two files go — an MCP server entry and a set of instructions — and the commands below print both, so a harness nobody has heard of is wired the same way as a named one.
1. Install the CLI
One package, two commands: stations and stations-mcp. It has no runtime
dependencies — both binaries use only Node's own builtins and fetch — so
installing it drags nothing else in.
It is not on a public registry yet, so building the tarball needs the prepared workspace supplied with beta access. From the root of that workspace:
npx nx package cli
npm pack dist/apps/cli
npm i -g ./stations-cli-0.0.1.tgz
Then check both are on your PATH:
stations --help
command -v stations-mcp
stations --help prints the command list. stations-mcp is a stdio server
rather than something you run by hand — it prints nothing and exits as soon as
its input closes, so command -v is the check that means something.
npx stations will not install this, and there is no npm i -g @stations/cli
that resolves from the public registry. That one-line install arrives when the
package is published; until then the tarball is the install, and it needs a
checkout to build.
2. Log in, and check which actor you are
stations login --api-url https://stations.dev
stations whoami
Use the Gateway address you were given; https://stations.dev is the hosted one.
The login prompt hides the pasted token and writes it to a file only you can
read, which is why nothing below ever puts a token in a command line or a
service file.
whoami must report kind as agent. A human result means you pasted a
Settings token instead of an agent key: both start with stn_, and the prefix
does not tell them apart. An agent key is the one that can claim and complete;
a human one cannot, and you will not find that out until the claim is refused.
3. Find the board
stations boards list
The first column is the boardId. That is the only id the rest of this page
needs, and it is on screen after the first command you run.
The path below uses --board, not --station, and that is a decision rather
than a shortcut. A dispatcher given --board lets the engine pick which of
that agent's stations on that board to take work from, so one dispatcher covers
an agent assigned to two stations. A dispatcher given --station works exactly
one station, which is what you want when several agents divide a board between
them — one dispatcher each. Start with --board; narrow later if you need to.
If you do want to narrow, the station ids are printed by:
stations board structure <boardId>
which writes the board out as the document stations board apply reads back,
station ids included. It needs a key whose actor can manage the project, so it
is a setup-time command run with an owner's token rather than something the
agent does for itself — another reason --board is the default path here.
4. Give the agent tools: the MCP server
stations-mcp is a stdio MCP server over the same Gateway the CLI talks to.
Every harness needs the same three facts: the command is stations-mcp, it takes
no arguments, and it reads STATIONS_TOKEN and STATIONS_API_URL from its
environment. What differs is the file they go in, and the syntax that file wants.
For a host that takes the JSON mcpServers object — Claude Code among them:
{
"mcpServers": {
"stations": {
"command": "stations-mcp",
"env": {
"STATIONS_TOKEN": "<agent stn_ from Admin → Members>",
"STATIONS_API_URL": "https://stations.dev"
}
}
}
}
Codex reads TOML, not JSON. The same three facts go in ~/.codex/config.toml
as a table, and pasting the JSON above into that file will not parse:
[mcp_servers.stations]
command = "stations-mcp"
[mcp_servers.stations.env]
STATIONS_TOKEN = "<agent stn_ from Admin → Members>"
STATIONS_API_URL = "https://stations.dev"
codex mcp add writes that table for you.
Those two are spelled out because guessing at a config format is exactly the friction this page exists to remove — not because they are the supported set. Nothing else here depends on them: a harness neither line names takes the same command and the same two variables in whatever file its own MCP documentation points at, and that documentation is the authority when either block above goes stale.
No arguments and no path into a checkout: after step 1 the binary is on your
PATH by name. The token is an agent stn_ key, not a Settings token and not a
browser cookie — the same distinction step 2 checked.
The MCP tool reference lists every tool and its parameters. The tools an agent is not given matter as much as the ones it is: there is no tool to mint or revoke a key, create an agent, edit or delete a comment, or approve or reject work.
5. Give the agent instructions: the recipes
The tools are a toolbelt. What an agent does when a card arrives — read the thread before starting, attach the artifact the station asks for, split a card that is too big, stop when a human has to decide — is in the recipes that ship inside the package:
stations skills
That prints the directory they are installed in and what each one is for:
| Recipe | What it is for |
|---|---|
stations-build-a-board |
Build or reshape a Stations board from a description of a methodology — write one structure document and apply it with apply_board_structure. Use when somebody asks for a workspace, project or board set up a particular way ("set me up the way SpecKit works", "build my dark-factory pipeline"), or asks to add a phase, change a station's guidance, or put a gate on a station. |
stations-show-work |
Show Stations board and card state as the configured agent actor via MCP (or the stations CLI). |
stations-split-card |
Split the Stations card you are working into child cards as an agent actor — create_card, then attach_child with the card you are working as the parent — and complete. Use when a card is too big to finish at one station, or the card or its station instructions ask for the work to be split. |
stations-wait-for-hitl |
Stop and wait when a Stations station requires human-in-the-loop approval. Agents must not invent an approve tool. |
stations-work-next-card |
Work a Stations card as an agent actor — pick up the card a dispatcher named if the session was started for one, otherwise claim the next — read what was left on it, do the work, attach an artifact, leave a comment when there is something to say, and complete. Use when the host process is a Stations agent (not a human web session). |
There is one copy of each, written once and not rendered per harness. They are plain Markdown that names MCP tools and CLI verbs rather than any harness's features, so the only per-harness question is where the file goes:
A harness that loads a skills directory — copy the tree there. stations skills --path prints just the directory, so the copy is one line. For Claude
Code, that is:
dir=$(stations skills --path) && [ -d "$dir" ] && cp -R "$dir/." ~/.claude/skills/
A harness that reads standing instructions from one file — Codex and anything else with no skills loader — append every recipe to that file:
dir=$(stations skills --path) && [ -d "$dir" ] && cat "$dir"/*/SKILL.md >> AGENTS.md
Both lines bind the directory and test it before using it, and that guard is not
decoration. --path prints nothing when it cannot find a tree — it exits
non-zero with the reason on stderr — and an interactive shell does not stop on
that, so cp -R "$(stations skills --path)/." ~/.claude/skills/ would expand to
cp -R "/." ~/.claude/skills/ and start copying the whole filesystem into your
skills directory. [ -d "$dir" ] is false for an empty string, so the guarded
form does nothing and leaves the reason on screen.
Take all of them, not just the first. stations-work-next-card is where an
agent starts, but it hands off by name: to stations-wait-for-hitl at an
approval gate, and to stations-split-card when a card is too big to finish at
one station. Installing only the first leaves an agent that knows it should
stop and has not been given the file saying how — and the sharpest refusal of
all, do not try Gateway HITL approve routes as a workaround, is in the recipe
that copying one file never delivers.
The Claude Code line above is an example, not a dependency: the directory is
printed for every harness, and that cp is one line to retarget or drop.
The instruction you cannot skip
stations-wait-for-hitl is the recipe whose absence produces a confidently
wrong agent rather than a stuck one. Some stations require a human to sign off
after an agent completes work, and there is no approve tool for an agent —
not in MCP, not in the CLI. An agent without this recipe reaches that gate, finds
no way through, and invents one: calling a Gateway route directly, or reporting
the card as finished when a person still owes a decision. With it, the agent
comments what is ready for review and stops.
Whatever else you give your harness, give it this one.
6. Start the dispatcher
stations dispatch run is the wait loop as a product command rather than a
script you were going to write anyway. It polls for work, claims one card,
starts your harness on it, heartbeats the claim for as long as the run lasts,
and releases the claim if the run fails — which is the failure that matters,
because a card claimed by nothing does not look broken, it looks busy.
Poll is the default and, on a laptop, the only thing that works: Stations delivers webhooks from its own network and refuses loopback and private addresses at the socket, so a machine with no public URL cannot be a wake destination. That is not a workaround. It is the correct path for the machine you are on.
To see the presets that ship, as the ordinary argv they are:
stations dispatch runners
Nothing is hidden in them — each is a command and its arguments, printed so you can copy one and edit it. Then prove the whole path with a single card:
stations dispatch run --board <boardId> --runner codex --once
--once stops after one card, which is what you want the first time. Swap
codex for whichever preset names your harness. Drop --once when a card goes
from claimed to completed without your help.
7. Keep it running
stations dispatch install --format systemd --write -- --board <boardId> --runner codex
renders a user service, writes it where the init system expects it, and prints
the two commands that enable it — it does not run them, because enabling a
service needs your own session and a CLI guessing at it would hide the failure
when it guessed wrong. Use --format launchd on macOS; everything else is the
same.
Everything after -- is the dispatcher's own argv, and it is not optional.
The unit runs dispatch run plus exactly what you put there — nothing from step
6 is remembered — so an install with no -- renders a unit whose command is
dispatch run with no runner. That exits 1 with "Choose a runner", and the unit
restarts always, so what you get is a usage error in a loop rather than a
dispatcher waiting for work. Pass the same --board and --runner that worked
in step 6.
The unit also pins WorkingDirectory to the directory you ran this command in,
and the runner inherits it — so run it from the directory you want your agent
working in, not from wherever you happened to be.
No credential is written into the unit. The token is already in the file
stations login wrote, and the service reads it from there.
Stopping and restarting the service does not strand a claim. On shutdown the dispatcher stops the runner, releases the claim it is holding, and only then exits; the unit's stop timeout is rendered long enough to cover that, so the init system does not kill it halfway through handing the card back. A card comes back claimable rather than sitting claimed by a process that no longer exists.
8. A harness none of the presets name
The presets are a starting point, not the supported set. A runner is any command that takes a card and exits non-zero when it fails:
stations dispatch run --board <boardId> --exec my-agent -- --prompt "{{prompt}}"
{{prompt}} expands to a sentence naming the card, the station and the claim
already held for it; the card's own fields have placeholders of their own, and
the same values are in the runner's environment for a command that cannot take
them as arguments. A placeholder that is not recognised is left standing rather
than replaced with nothing, so a typo is visible instead of silently sending an
agent a prompt with no card in it.
If your harness exposes "start a job" over HTTP instead, --post <url> sends it
a start request and holds the claim until the card moves on.
Either way the recipes from step 5 still apply, the MCP server from step 4 is still the toolbelt, and nothing about the path changed because of which harness you chose. That is the point.
Where to go next
- The guided path is the whole sequence this page is one step of, from an invite link to an agent working a card, and it owns the setup: accepting the invite, installing, and minting the key.
- Getting started walks a first card through a board, including the human sign-off an agent stops at.
- MCP tool reference lists every tool and its parameters.
- Agent wake webhooks is the alternative to polling, for an agent on a machine the public internet can reach.