MCP tool reference
Stoat serves 91 tools over one streamable-HTTP MCP endpoint:
http://localhost:5526/api/mcpStateless, so every call is a self-contained request — a dropped connection costs nothing, and several agents share one server with no sessions to keep straight.
Conventions
- Naming a plan and a node. The plan tools (
get_plan,plan_status,get_node, the authoring tools) name a planslugand a nodeid. The work-loop tools (next_ready_nodes,claim_node,report_node,release_node,ask_person, theawait_*tools) name themplanandnode. agentis your identity, and every write takes it: it is recorded on what you assert, so pick one name and keep it.sessionis your Claude Code session id ($CLAUDE_CODE_SESSION_ID). Pass it when you claim: it is what links your claim to your session on the agent roster.- Every tool's full schema comes from
tools/list. A call that misses a required argument, or names one the tool does not take, is answered with what is missing and what the tool takes, so the next call can be right. - Refusals are results, not errors. A tool that will not do something says why in a JSON object with an
error({"error": "no repository 'JasperFx/x' — register it with register_repository first; …"}).
Profiles
/api/mcp serves every tool, for supervising sessions and people's own agents. /api/mcp?profile=worker serves the 16 a run-plan worker uses, and is what the runner hands every agent it launches: claim_node, report_node, release_node, report_node_step, ask_person, await_dependencies, await_package_version, await_run_completion, report_activity, submit_review, failing_tests, failing_spec, flaky_ledger (a red gate's worker diagnoses with them before it retries, stoat#270), recall, record_memory and declare_repository_pack (a worker that worked out how its repository packs records it, stoat#217). Its tools/list is under a quarter of the full surface's (about 15.7K characters against 69K, measured when the profile had 12 tools and the full surface 71), and every byte of it rides every call a worker makes. A tool outside the profile is not listed and cannot be called; an unknown profile name is a 400, never the full surface. declare_agent and get_node are left out on purpose: the runner declares its workers itself, and get_node is a supervisor's read (the prompt carries the node). record_memory stays in, so workers, the sessions that hit the gotchas, keep the learning loop.
On its own machine every tool is free. A Stoat that other machines reach is Stoat Team: once the licence check is enforced, a caller on another machine with no active licence gets a readable license_required refusal from every tool. The 0.6.0 preview does not check it, so every tool works from anywhere; see Installation.
Plans — 30 tools
A plan is a cross-repository DAG. Nodes are GitHub issues, PRs, NuGet and npm publishes and consumes, test-run gates, Event Modeling slices and reviews; edges are dependencies; status is derived from observation, never asserted.
Reading
| Tool | What it does |
|---|---|
list_plans | Every active plan. Use the slug with get_plan / plan_status. |
get_plan | One plan's full DAG — nodes, depends_on edges, and dependencyOrder (every node listed after everything it depends on). |
plan_status | The live view: each node's derived status — unrealized, unknown, missing, open, claimed, pr-open, abandoned, done, waiting, running, failed, mismatch, partial, and stale for a gate that must be verified again, findings / fixing for a review. An issue or pr node carries prs: each pull request's number, URL, state, CI with the failing checks named, review decision and mergeability, what the card's PR line shows. The plan's runner rides it (none, or its state, who paused it and when, parallel, slotsInUse), and each ready node the runner would launch carries launch: why it is not being launched (paused, waiting-for-slot, no-runner, …) or next. |
plan_usage | What a plan's agents have spent: cost and tokens per model, then per node, biggest first. Cache reads and writes are their own figures. A floor: a killed attempt reports nothing. |
get_node | One node, zoomed in: the same view a person gets by clicking its card. Its plan_status entry under node, then readiness (why it is or is not ready, in words: waiting on \fix`, ready, awaiting a go-ahead), its brief, url, the latest claim(agent, since, session, lease, live or not, steps), thequestionsasked on it, people'snotes, a gate's gateRuns, the plan runner's callouts about it with the resumeline when one is known, its owntimelinelines,dependsOnandunblocks(direct, across plans), and arecallof its repository's notes and the memories filed under the plan and that repository.includeGitHub: trueanswers, adding the issue's body, labels and recent comments, and each PR's description, diff stat and checks, a failing GitHub Actions job with the tail of its log; cached for a minute. Same view as [GET /api/plans/{slug}/nodes/{id}`](/reference/rest-api#one-node). |
plan_history | Every change ever made to a plan, oldest first: who, when, what. |
next_ready_nodes | Ready work across every plan or one — dependencies all done, not itself done/missing/running. Sorted by leverage (how many nodes it unblocks). Each entry carries its plan's runner and the node's launch, the same fields plan_status has. |
Authoring
| Tool | What it does |
|---|---|
create_plan | An empty plan. Status derives from observation immediately. repoBranches names the branch each repository's consume pins are read from, alias or org/name → branch (the branch a pin is read from). model and skills say what its run-plan workers start with (what a worker starts with). |
change_plan | Title, anchor, repo aliases, repoBranches (replaced wholesale; {} clears them), and branch, the plan's integration branch ("" clears it), and its workers' model ("" clears it) and skills ([] clears them). Omitted values keep their current value. |
declare_plan_resources | The infrastructure a plan's nodes share and each one's capacity, { "postgres": 1 }, replacing what the plan had. Nodes name what they hold with uses, and run-plan launches a node only when every resource it uses has room. A resource still in use cannot be removed. |
add_plan_node | Kinds: issue, pr, publish, consume, test-run-gate, slice, review, person. An issue number is optional — unbound means unrealized. Refused with the reason when it would break the plan. Also takes the node's brief (Markdown instructions the runner puts in the agent's prompt), a gate's steps ([{ id, title?, run?, cwd?, env?, timeout? }]: cwd relative to the repository root, env a map that never sets BOBCAT_RUN_TAG, timeout a duration such as 20m or 00:20:00), its optional repo (the checkout its steps run in) and reverify: on-branch-change, a consume node's branch (where its pin is read, over the repo's declared branch), the resources it uses, and placement: cloud to keep it for a cloud agent (agent: cloud). With checkout naming a local clone, a dotnet test step is checked against it and any problem comes back as warnings, never a refusal. merge: merge-on-green is refused here: only a person turns it on. model (the runner-launched kinds): the model this node's worker asks for, over the run's and the plan's; skills, the skills its work uses. assignee: person keeps a node for a person, exclusive: machine keeps the machine to itself while it runs, a local publish rehearses the real one, and a consume's bumpedBy names the node that moves its pin. |
change_plan_node | Replaces a node wholesale; anything omitted is cleared. Use link/unlink for edges only. |
remove_plan_node | Refused while others depend on it — dropping an edge is a decision, not a side effect. |
link_plan_nodes | Add an edge. Refused if it would make a cycle. |
unlink_plan_nodes | Remove an edge. |
bind_plan_node | The materialize step: bind an unrealized node to the GitHub thing you just created. |
archive_plan | Out of the active set; history stays. |
import_plan | A whole plan as YAML (schema 1). Diffs into an existing plan, so re-importing the same document records nothing. Takes checkout for step warnings like add_plan_node, and refuses to move a node to merge-on-green. |
export_plan | One plan as a YAML document — the shape import_plan takes, so importing it back records nothing. |
Claims
| Tool | What it does |
|---|---|
agent_roster | Every Claude Code session on the box, and every agent that declared itself on any box: running / idle / stopped (with cause), what each holds, and whether anyone is actually working. Each entry's provenance says whether its liveness was observed or only declared, and activeAt when it last did anything (its latest tool call, from memory). Same derivation as GET /api/agents. |
claim_node | Claim or renew. A lease (default 30 minutes), so a dead session does not hold a node forever. Refused with the holder's name when taken. A cloud agent passes where: cloud and its session's link. |
report_node | Attach or replace your note and renew the lease. For decisions worth surfacing, not a transcript. A cloud agent says its session's link here once it knows it. On an issue node, pr (a number in the node's repository) links the pull request you opened, however its description is worded: GitHub attaches a PR to its issue only through a closing keyword, so a Part of #n PR is otherwise invisible to the plan. A linked PR rides the card, follow-ups and merge-on-green, and never closes the node (the issue does). The answer says prLinked, or prErrors when it was refused. The REST twin takes pr in the same body. |
ask_person | Ask a question about the node you hold, with optional short options and needs (agent: the plan's lead agent may answer; person: only a person may; left out, the lead while one is alive, else a person), and park until it is answered, at most about four minutes per call (Stoat:AskPersonParkSeconds, default 240, under a client's tool timeout; your lease is renewed). recommended says what you go with if nobody answers, shown as your default. Returns answered with the answer, who gave it and byLead, or pending (pending: true with its questionId) when nobody has answered yet: call again with that questionId to keep waiting, and do not carry on with your recommendation meanwhile unless your question said you would. unanswered (recorded on the question, which stays open) only when you passed a timeoutSeconds shorter than the park limit. A worker that ends its run with a question for a person still open spends no attempt: the runner keeps the question on the plan, and the answer resumes its session. One open question at a time; it never changes the node's status. |
submit_review | For the review node you hold: your findings on the integration branch, each { title, brief, severity?, steps? }. For a red test-run gate you hold (stoat#270): your diagnosis, one finding per cause, each also with cause (product, test, environment, tooling, flaky, lane-clash; product needs repository), repository?, fixKind? (person for an environment) and evidence?: { failingTests, step, framework, rerun }; a person accepts one as an unbound issue (or a person step) the gate waits on. Findings are proposals — a person accepts (it becomes a fix node the review depends on), edits or rejects each one, and the review is done only when they have; an empty list still needs a person's sign-off. Submitting again replaces the undecided findings. |
declare_node_steps | The steps you mean to take on a node you hold, { id, title, gate?, runTag? }, so the agent view shows what is and is not complete. gate names a repository gate the step runs; runTag names the BOBCAT_RUN_TAG whose run is the step's observed evidence. Steps live and die with the claim. |
report_node_step | Where one step stands: pending / running / done / failed / skipped, with a note and, for a gate, the exit code it returned. Reporting the current state again records nothing. |
release_node | Status stays whatever observation says: finishing the work is proven by the issue closing, the version appearing, the suite passing. |
report_gate_result | A test-run gate's result in your own words, for a suite that reports to no Bobcat monitor and could not be run through stoat gate exec. Asserted, not observed: recorded and shown as yours, and it decides the gate only where a person allowed asserted results on it from the plan page. The answer says whether it counts. Prefer stoat gate exec, which Stoat observes. |
Steps never move a node's status
A checklist is the agent's own word. It shows a person watching the roster how far along the work is, and it is never a second, softer definition of done. A node is done when its evidence says so (the issue closed, the version published, the specs green), even if every step reads done before then. Where a step names a runTag, the run's verdict is shown beside the agent's state, never in place of it.
Parking
| Tool | What it does |
|---|---|
await_dependencies | Block until every dependency of a node is done. Returns ready, or timeout with what is still outstanding. |
await_package_version | Block until a feed serves >= minVersion. Queries the feed directly, so it works for any package, not only ones a plan declares. |
Every await_* tool (these two, await_memories, await_run_completion and the lead's await_questions) waits 240 seconds by default, under the time an MCP client commonly allows a tool call, and answers a timeout rather than being cut off. On a timeout, call again.
Agents — 4 tools
How an agent gets onto the roster when Stoat cannot see it: an agent on another machine, or a non-Claude agent on this one. The session files are local by construction, so for those a push is the only way in. A declaration is asserted and labelled as such, and it is a lease, so an agent that stops declaring ages off the roster as lapsed rather than lingering.
| Tool | What it does |
|---|---|
declare_agent | Put yourself on the roster: session id ($CLAUDE_CODE_SESSION_ID for Claude Code, so it joins the session and its claims), host, cwd, repository, intent and activity. Lease default 10 minutes, max 60. A live session id another agent declared is refused with the holder's name. |
report_activity | Say what you are doing now, and renew. An unchanged report in the first half of the lease records nothing, so heartbeating is cheap. |
depart_agent | Leave in an orderly way: reads as departed, not lapsed. Does not release your claims. |
session_journal | What a session did, oldest first: starts and ends (and why), commits and pull requests (from hooks), the nodes it claimed with its notes, the decisions it recorded, its runs — each saying where it came from. Read it before picking up a node another session held. |
A declaration never overrides an observation
Where the box also observes the session, the observation wins on liveness, both ways: a declaration can't keep a session the probe found stopped alive, and a lapsed declaration can't stop one the probe sees running. The declaration adds only intent and activity.
Leading a plan run — 5 tools
A plan run in practice has a supervising agent session working with the person. These make that role explicit: the lead is the first responder for its workers' ask_person questions marked agent, and escalates the rest. Stoat records the lead and routes to it; it never depends on it. A lead can also work the plan itself, with no runner: see Stoat as a work queue for a lead agent.
| Tool | What it does |
|---|---|
lead_plan | Take the lead of a plan's run with your session id. Refused while another lead's session is alive; a person can clear a lead from the plan page. |
release_plan_lead | Step down. The plan's questions go to the person from then on. |
await_questions | As the lead, park until a worker's question you may answer is open, and return them all. Says not-routed at once when Stoat does not see your session alive, since nothing would ever reach you. |
answer_question | As the plan's live lead, answer a question marked agent. The parked worker wakes with your answer, recorded as the lead's. Without a questionId, and with relayFor naming the person, it relays that person's answer to an exited worker's question, which the plan runner resumes it with; on a runner's fix callout the relayed reply is one more fix attempt, recorded as the person's with the lead named (stoat#263). |
escalate_question | As the lead, hand a question to the person, with why: it is theirs from then on, on the strip and in notifications. |
The lead is an agent, and holds none of a person's checkpoints of its own
A question marked person is never answerable over MCP, by the lead or anyone. The go-ahead, merge policy and the person's other controls are the person's: the lead may only relay a decision the person made, recorded as theirs. The runner's commands, dismissing a question and purging a plan have no MCP twin at all. And the lead's liveness is the session probe's answer: a lead whose session is gone never leaves a question unrouted, since every question it held is the person's at once, and no relay is accepted from it.
Relaying a person's decision — 18 tools
Every control the plan page keeps for a person has an MCP twin that only the plan's live lead may call (stoat#244): the session that took lead_plan, while the session probe sees it running, idle or awaiting input. The lead relays what the person decided in its conversation; it holds no checkpoint of its own. Each takes agent and session (the lead's) and relayFor (the person, as they would sign it), sends the very command the REST endpoint sends, and records the person as by and the lead as relayedBy on the event. The plan's Activity names both ("approved for agents, relayed by net11-lead · Jeremy D. Miller").
Refused, each with a test named for it: an agent that is not the plan's lead, a lead whose session is gone, a relayFor that is missing or names the lead itself, and a decision about a node the lead holds a claim on (a worker still cannot approve or merge its own node). A repository's controls are relayed only for a repository the plan works in. None of these is in the worker profile.
| Tool | What it does |
|---|---|
approve_for_agents | Give issue nodes the go-ahead for agents to claim them, or withdraw it. A node already done, approved or not bound is skipped and named, as the plan page's "approve all" does. |
set_merge_policy | manual-review or merge-on-green for an issue, pr or consume node's PR. Keeps the go-ahead. |
accept_partial | Accept a node's reported partial work as enough, or sendBack an acceptance. |
mark_person_node_done | Mark a person's node done, or undo it. Refused for any other node. |
expedite_node | Ask for a node to run next, or withdraw it. |
set_machine_use | Mark a node heavy or exclusive, keeping its go-ahead. |
mark_for_cloud | Keep a node for a cloud agent, or withdraw a person's mark. |
reverify_gate | Ask for a done test-run gate to be verified again. |
accept_stale_gate_pass | Accept a gate's stale pass (dirty tree, or behind its release branch) as it stands, or withdraw that. A newer run replaces it. |
allow_asserted_results | Let a test-run gate be decided by an asserted result, or withdraw that. |
set_agent_cap | Cap the agents holding claims on the plan at once; no cap clears it. |
set_realize_issues | Let the plan's runners create the issue for an approved unbound node, or stop them. |
propose_base_fix | Add the fix node for a default branch a node's merge turned red, as a person (stoat#260); answers the existing fix if there is one. |
add_person_note | A person's note on the plan or a node. |
decide_review | accept or reject a review finding, or sign-off a review that found nothing. |
set_repository_ci | Turn a repository's CI off on purpose, or back on. |
set_gate_stand_in | Let a repository's commit or push gate stand in for CI, or take that back. |
set_session_limit | Set a repository's or the machine's session limit; 0 is the hold, release restores the limit before it. |
Repositories — 15 tools
The cross-plan knowledge base. Check here before asking a question a previous session answered.
| Tool | What it does |
|---|---|
list_repositories | Every repository: slug, what it publishes and consumes, CI workflows, dependencies, note count. |
get_repository | One in full, including every markdown note with who wrote it and when. |
register_repository | Slug is owner/name — the same shape issue refs use. |
describe_repository | Description or default branch. |
declare_repository_package | This repository publishes or consumes a package — the cross-repo edge planning runs on. Re-declaring a known fact is a no-op. |
retract_repository_package | Removes it from publishes and consumes both. |
declare_repository_ci | A gating check name — the expected-check list PR status is read against. |
retract_repository_ci | |
declare_repository_pack | How the repository packs its NuGet packages to a local folder feed (stoat#217): a command with {version} and {feed}, such as ./build.sh Pack --version {version} --feed {feed}. The plan runner runs it for a publish node on a folder feed, with no agent and no person's OK. Re-declaring replaces it. |
declare_repository_gate | A repository-specific gate: a build target an agent runs before committing or pushing, like CritterWatch's ./build.sh CommitGate, with what it is made of. Not a test run and not a CI workflow. Stoat records it and never runs it. Re-declaring by name replaces it. |
retract_repository_gate | |
declare_repository_dependency | Repository-to-repository, independent of any one package edge. Say why in reason. |
retract_repository_dependency | |
add_repository_note | A gotcha, a convention, a decision. Append-only, recorded with who and when. |
archive_repository | Out of the active set. The stream — and every note — stays. |
Memory — 10 tools
What one session learned, the next one recalls.
| Tool | What it does |
|---|---|
recall | Free-text search, best match first. Call it before starting a node, and before asking something a previous agent may already have answered. |
get_memory | One in full — and, when asked, every version from its stream with who changed it and why. |
list_memories | Browse without a search term: filter by scope, kind, tags; newest change first, paged. |
record_memory | What the next session should know. Not a progress note — that is report_node, and it is about the node you hold right now. |
revise_memory | Only what you pass changes; earlier versions stay on the history. |
refile_memory | Move to another scope — session → repository, once it turns out to be about the repository. |
link_memories | A typed edge: example-of, supersedes, explains, contradicts. Recorded on the first, readable from both. |
archive_memory | Hidden from recall and listings; history stays. |
restore_memory | Back into recall and listings. |
await_memories | Block until a memory is recorded or changed in a scope — how an agent hears what another session just learned, without calling recall in a loop. |
Recall is a full-text search over title, tags and body: every word must match, a word also matches the start of a longer one, and the best match comes first.
Runs and evidence — 9 tools
These read the runs Stoat's receiver on 5527 has been sent. A suite that publishes only to bobcat ui is invisible to them; see Runs and evidence.
| Tool | What it does |
|---|---|
list_runs | Every run: running, finished, and orphaned (rehydrated from an archive after its publisher died). Newest first. |
run_status | Per-scenario outcomes, the executing scenario's live steps, retry activity — and for a supervised run the worker lanes, recycles, faults, stalls and progress. |
failing_tests | Failed and aborted scenarios with error messages and failing steps. Each failing step carries its failedCells (named expected/actual pairs) and its exception (type and filtered frames) when the publisher sent them. An empty list means nothing has failed so far — check run_status for whether it is done. |
flaky_ledger | Scenarios that passed only after retrying. Green builds hiding real instability; spans every known run unless narrowed. |
await_run_completion | Block until a suite settles. Returns finished, orphaned or timeout with the final summary. |
export_run | ctrf (rich — retries, flakiness, steps) or junit (lossy, for CI ingestion). |
event_model | Every slice with its command, handler, aggregates, emitted events, read models, messages and bound specifications. Empty until a producer pushes one. |
slice_coverage | What is untested per slice, and the two gaps are kept apart because they imply different actions: no-spec (nothing bound — scaffold scenarios) and no-evidence (bound, but no run has exercised it). |
failing_spec | Everything failing_tests summarizes away: every step of the final attempt with status, duration and error, its cells (green ones too — a wrong sum must not hide a right product), logs, diagnostics and exception with filtered frames, plus prior attempts. This is the input for writing the code a red spec describes. |
TIP
With several publishers active, a no-runId await_run_completion latches whichever run is in flight — which may be another session's supervisor worker. Pass the runId from list_runs.
See also
- An agent's first session — these tools in the order a session uses them
- Stoat as a work queue for a lead agent — the lead and relay tools in use
- REST API — the same surface for a client that has no MCP
- Ports and wire contracts — the run console's wire, on the other port