MCP tool reference
Every Stations MCP tool, with its parameters and required inputs.
GENERATED FILE — do not hand-edit. Source:
scripts/lib/docs-mcp-reference.ts. Regenerate withnpm run docs:reference, thennpm run docs:content.
The MCP process is an agent actor, not a human session. After you have beta access, use the stations-mcp binary that ships with the workspace — it is not published on the public npm registry, so npx stations-mcp will not install it. Mint an agent stn_ key in Admin → Members (not a Settings PAT and not a browser cookie). The tool reference documents every available tool and its parameters. Agent tools cannot mint or revoke keys, create agents, approve or reject work, or edit or delete comments.
{
"mcpServers": {
"stations": {
"command": "stations-mcp",
"env": {
"STATIONS_TOKEN": "<agent stn_ from Admin → Members>",
"STATIONS_API_URL": "https://stations.dev"
}
}
}
}
whoami
Return the STATIONS_TOKEN actor (actorId, tenantId, kind, actorKind). Agent keys report actorKind: agent.
No parameters.
list_boards
List boards visible to the token actor (Gateway /bff/dashboard).
No parameters.
show_board
Show one board by id or slug.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| boardId | string | Yes | Board id or slug | — |
list_cards
List cards on a board.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| boardId | string | Yes | Board id or slug | — |
show_card
Show one card by id or identifier (e.g. SHIP-12): its station, claim, artifacts, children/parents, and two separate histories. comments is the discussion thread — sentences people and agents left on the card, oldest first, each with an author name and kind — including ones written before the card reached your station. timeline is the audit trail — system history such as arrivals, claims, completions and rejections. They are distinct: do not read the timeline as discussion or the thread as a record of state changes. A rejection’s reason appears once, as the comment whose id the station.rejected timeline event names in reasonCommentId. To read only the thread use list_comments; to add to it use add_comment.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| cardId | string | Yes | Card id or identifier | — |
claim_next
Claim the next available card at a station (agent work).
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| stationId | string | No | Optional station id | — |
| boardId | string | No | Optional board id | — |
claim
Claim a specific card.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| cardId | string | Yes | — | — |
heartbeat
Renew a claim you hold. You do not normally need this: the Stations MCP server keeps the lease on every card it claims for you, on its own timer, for as long as this session is running — so a long piece of work does not lose its claim and you do not have to interrupt it to say you are still here. Reach for it only when a claim was not handed to you by this server (it was taken before this session started, or the server was restarted while you held it), or when a tool result has told you your claim is gone and you want to check. Pass claimId. Without one it renews the single claim you hold and refuses to guess when you hold several.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| claimId | string | No | The claim to renew, as `claim` or `claim_next` reported its id. Required when you hold more than one card | — |
complete
Complete a claimed card. If the station requires HITL, stop — do not approve.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| cardId | string | Yes | — | — |
attach_artifact
Attach an artifact to a claimed card (url, text, or file value).
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| cardId | string | Yes | — | — |
| key | string | Yes | — | — |
| kind | string | Yes | — | {"enum":["url","text","file"]} |
| value | string | Yes | — | — |
| contentType | string | No | — | — |
create_card
Create a new card on a board. The card lands at the board’s first station as ordinary, claimable work with its own identifier and lifecycle. Use it to split the card you are working into smaller pieces: create one card per piece, then call attach_child with the card you are working as parentCardId and the new card’s id as childCardId. boardId is a board id or slug from list_boards, in your own tenant; children may live on any board in the same project as the parent. This does not claim the new card and does not relate it to anything — attach_child does that.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| boardId | string | Yes | Board id or slug the new card is created on | — |
| title | string | Yes | Card title | — |
| description | string | No | Optional card description (what this piece of work is) | — |
attach_child
Make one card a CHILD of another. Direction matters: parentCardId is the card that WAITS — the card you are working and splitting, whose station may refuse to finish until every child is done. childCardId is the smaller piece of work (usually one you just made with create_card) that the parent waits on. Attaching backwards makes your new child wait on the card you are working, so when in doubt: parent = the card you claimed, child = the card you created. Both cards must be in the same project; a card cannot be its own child, and a cycle or an over-deep chain is refused. Returns the relation. To undo it, call detach_child with the same parentCardId and childCardId.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| parentCardId | string | Yes | The card that waits (the one you are working and splitting). Card id or identifier such as SHIP-12 | — |
| childCardId | string | Yes | The piece of work the parent waits on (usually just created with create_card). Card id or identifier | — |
detach_child
Undo attach_child: remove the child edge between a parent card and one of its children. parentCardId is the waiting card; childCardId is the child to release. Only the edge is removed — the child card itself is not deleted, closed, or moved. Use it when a split was wrong; use complete on the child when the work is actually done.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| parentCardId | string | Yes | The parent (waiting) card. Card id or identifier | — |
| childCardId | string | Yes | The child to detach from that parent. Card id or identifier | — |
list_comments
Read the comment thread on a card: what people and other agents wrote on it, oldest first, including sentences left before the card reached your station. Each comment carries the writer’s actor id as authorActorId (compare it with your own actorId from whoami to find your earlier notes) and when it was written. Discussion only — the audit trail (arrivals, claims, completions, rejections) is timeline on show_card, and a rejection’s reason is the comment that event names. Read it before starting a card: it is where a previous actor explains what they did, what they skipped, and why.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| cardId | string | Yes | Card id or identifier such as SHIP-12 | — |
add_comment
Append one comment to a card, as yourself: the author is your agent actor, and a human sees it in the card’s ticket thread. Leave a sentence when there is something the next person or agent on this card would want to know and cannot see from the artifact: part of the work you skipped and why, a blocker you hit, a place you deviated from the station’s instructions, or what is ready when you stop for a human. Keep it short and specific. Do not write “done” — complete says that — and do not repeat what the artifact already shows. Append-only: a comment cannot be edited or deleted by anyone, so write it once and correctly. It is not an audit event and does not change the card’s state. Work refuses an empty body and one over its length cap; the error names the limit.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| cardId | string | Yes | Card id or identifier such as SHIP-12 | — |
| body | string | Yes | The comment text, plain prose. Newlines are kept. Written once; it cannot be changed later | — |
show_board_structure
Read a board’s structure as the same document apply_board_structure accepts: its phases in order, each phase’s station, that station’s guidance, its human-sign-off and child-cards rules, and the artifact gates a card cannot leave it without. It carries the board’s project as well, and that is what lets the document it returns be edited and applied straight back with nothing else: ids ride alongside the names and are ignored on the way in, so the project is matched by its key and the board by its name. A board whose project cannot be read says so under incomplete, and applying that one back needs boardId. It also reports what is unfinished — a board with no phases, a phase with no station — under incomplete, because a half-built board is a permitted state and the only thing wrong with it is not being told. Read it before applying to an existing board: apply matches by name, so this is how you learn the names it will match.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| boardId | string | Yes | Board id or slug. | — |
apply_board_structure
Build or reshape a board from one document describing the workflow you want: the phases cards pass through, the station in each phase, what that station tells a worker to do, whether agent work there stops for a human sign-off, whether a card must be split into finished child cards first, and which artifacts it will not let a card leave without. Use it when somebody describes a methodology — "set me up the way SpecKit works", "build my dark-factory pipeline" — rather than issuing a dozen separate creates. Reconciled, not replayed. It creates what is missing, updates what differs, and leaves alone what already matches, so running it twice with the same document changes nothing the second time and a run interrupted halfway is finished by running it again. It never removes anything. A phase or station on the board that your document does not mention is reported back under extra and left exactly where it is — a document you truncated by accident can never take a column off somebody’s board. Removing structure is a person’s job in the web setup UI. Matching is by name: project by key, board by name in that project, phase by name on that board, and a phase’s station by its position in the phase (so renaming a station is an update, not a second station). Call show_board_structure first when the board already exists, so you are matching the names it actually has. New phases are added after the ones the board already has. It cannot insert a phase before an existing one and cannot reorder them — Work advances cards by phase position and has no route to move one — so a document asking for either is refused with nothing written, rather than applied into a board whose cards would visit the stations in a different order from the document you wrote. Put new phases last, or have a person reorder the board first. Names have to be unambiguous, and that is refused before anything is written: a document that names one phase twice, a board that already holds twice a phase name the document uses, or a project holding two boards of the name you gave. Matching by name cannot tell any of those apart. For two boards of one name, pass boardId to say which you mean; the refusal lists their ids. Same-named phases your document does not name are left alone. A phase with no station, or a board with no phases, is permitted and comes back under incomplete rather than being refused. A brand-new board always arrives with one phase and one station, because that is what creating a board does; when your document does not name a first station, that step is left under extra rather than quietly filled in as the phase you asked for. Needs a key whose actor can manage Project; an agent key that lacks it is refused with 403.
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| document | object | Yes | A board’s intended structure. Reconciled, not replayed: `board apply` creates what is missing and updates what differs, and never removes anything a document leaves out. | See document below |
| projectId | string | No | Apply into this existing project. Use it instead of the document’s `project` block. | — |
| boardId | string | No | Reconcile onto this existing board rather than matching or creating one by name. | — |
| dryRun | boolean | No | Report what would change and write nothing. Nothing is created, including the project and the board. | — |
document
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| project | object | No | The project the board lives in, matched by `key`. Created when no project in the tenant holds that key. Omit it and pass `--project` / `projectId` instead when the project already exists. | See document.project below |
| board | object | Yes | The board itself, matched by `name` within the project. Work permits two boards in one project to share a name; if the project holds two of this one, apply refuses rather than picking, and `--board` / `boardId` says which you mean. | See document.board below |
| phases | array of object | No | The board’s phases, in the order cards pass through them. Phases already on the board and not named here are reported as extra and left alone. | See document.phases[] below |
document.project
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| key | string | Yes | Tenant-unique 2–6 character project key, e.g. `SPEC`. The match key, compared case-insensitively. | — |
| name | string | Yes | Project name. | — |
| description | ["string","null"] | No | Optional project blurb, used only when the project is created. | — |
| id | string | No | Ignored on apply — structure is matched by name, never by id. Present so the document `stations board structure` prints can be fed straight back. | — |
document.board
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| name | string | Yes | Board name. The match key. | — |
| description | ["string","null"] | No | Optional board blurb, used only when the board is created. | — |
| template | string | No | Optional first-party template slug from `GET /bff/board-templates`, used only when the board is created. Omit it when `phases` describes the board: apply then creates a blank board already named after the first declared phase and station, so nothing is left over to reconcile. A multi-step template’s own phases are matched by name like any others, and the ones a document does not name are reported as extra rather than removed. | — |
| id | string | No | Ignored on apply — structure is matched by name, never by id. Present so the document `stations board structure` prints can be fed straight back. | — |
document.phases[]
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| name | string | Yes | Phase name. | — |
| id | string | No | Ignored on apply — structure is matched by name, never by id. Present so the document `stations board structure` prints can be fed straight back. | — |
| stations | array of object | No | The phase’s stations, in order. An empty array or an omitted key is a phase with no station — permitted, reported as incomplete, never refused (#845). Work currently allows one station per phase and refuses a second itself. | See document.phases[].stations[] below |
document.phases[].stations[]
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| name | string | Yes | Station name. | — |
| id | string | No | Ignored on apply — structure is matched by name, never by id. Present so the document `stations board structure` prints can be fed straight back. | — |
| instructions | ["string","null"] | No | What this station asks of whoever works here. `null` clears it. Say what to do, and claim a gate only where `artifactRequirements` or `requireHumanApprovalWhenActorKindAgent` below actually sets one. | — |
| requireHumanApprovalWhenActorKindAgent | boolean | No | When true, work completed here by an agent actor stops for a human sign-off instead of advancing. | — |
| requireChildCardsDone | boolean | No | When true, a card may not leave here until it has child cards and all of them are done. The rule templates cannot yet declare (#790). | — |
| artifactRequirements | array of object | No | The gates. Replaces the station’s whole set: an empty array clears every requirement, and omitting the key leaves the existing set alone. | See document.phases[].stations[].artifactRequirements[] below |
document.phases[].stations[].artifactRequirements[]
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
| key | string | Yes | The artifact key a worker attaches, e.g. `pull_request` or `spec`. | — |
| kind | string | Yes | What that artifact is. `url`, `text` and `file` are attached by whoever holds the card. `attested_pull_request` and `attested_merged_pull_request` are not attached by anyone: the station holds the card until Stations has observed a pull request (or a merged one) naming it in a connected repository, and nothing typed can stand in for that. | {"enum":["url","text","file","attested_pull_request","attested_merged_pull_request"]} |