The Roscoe MCP server (started by roscoe mcp) exposes 32 tools to Claude
Code over stdio. They're grouped here by purpose. Tool names and input
schemas are the canonical surface: anything not listed here is not part of
the MCP contract.
All tools return JSON wrapped in MCP's content envelope. Error shapes are
not uniform across groups:
- Run tools set
isErroronly for genuine server failures and return{ error }for user errors. - Workflow-writer tools always set
isError: trueand return{ error, code }, wherecodeis aWriterErrorCode. test_workflowreturns{ ok: false, code, message }.
Treat code as the thing to branch on where a tool documents one, and don't
assume ok: false is present. The grouping below mirrors
apps/mcp/src/tools/*.ts.
Project scoping (cwd / global)#
Every tool below except the Docs group takes a scope input — exactly one of:
| Input | Type | Notes |
|---|---|---|
cwd |
string | Absolute path to any directory inside the project this call applies to — typically the user's IDE working directory. Resolved to the git root. |
global |
boolean | Set true to operate on the global ~/.roscoe store instead of a project. Use when there is no project directory (e.g. the Claude desktop app). |
The MCP server is long-lived and project-agnostic; the scope tells each call
where it applies. With cwd, the server resolves it to its enclosing git
root, derives the project id, and isolates workflows + runs to that project.
Two parallel calls with different cwds see different projects (per-call
AsyncLocalStorage isolation). With global: true, there is no project: tools
resolve from the global store, and runs are tracked in a shared global
namespace. Workflow create/move tools may simply OMIT source — it defaults
to repo when a repo source exists and global otherwise — so an explicit
source: "global" is optional in global mode, while source: "repo" there
returns no_repo_source.
Passing neither errors with CWD_REQUIRED; passing both errors with
CWD_GLOBAL_CONFLICT. The server never silently guesses a project.
For brevity, the per-tool input tables below omit the scope row.
Assume every project-scoped tool requires cwd or global.
Validation errors (returned as structured { error, code, cwd? }):
| Code | Meaning |
|---|---|
CWD_REQUIRED |
neither cwd nor global was passed |
CWD_GLOBAL_CONFLICT |
both cwd and global were passed |
NOT_ABSOLUTE |
cwd was empty or relative |
NOT_FOUND |
cwd does not exist |
NOT_DIR |
cwd is a regular file, not a directory |
PROTECTED_PATH |
cwd resolves into a protected system prefix (/etc, /usr, /System, …) |
NOT_GIT_REPO |
cwd is not inside a git working tree |
PROJECT_ID_UNAVAILABLE |
git remote URL and repo basename both unavailable |
Cross-project access. Every run-lookup tool — get_run_status,
advance_run, advance_consensus, advance_round_robin, advance_map,
resume_run, cancel_run, get_run_history — compares the run's
projectId to the caller's. A mismatch returns
"Run not found." rather than leaking that the run exists in another
project. Scope ownership is strict and symmetric: global-namespace runs
(projectId = '', started with global: true) are reachable only by a
global caller, and a project's runs only by that project's caller. This
matches list_runs, which filters by the caller's exact project, so the
by-id guard and the listing always agree.
The Docs tools (list_docs, get_doc, search_docs, ask_docs,
answer_docs) are project-agnostic and do not take cwd.
Index#
| Group | Tools |
|---|---|
| Workflow CRUD | list_workflows, get_workflow, create_workflow, update_workflow, delete_workflow, list_workflow_references, move_workflow, duplicate_workflow, rename_workflow |
| Authoring helpers | get_workflow_schema, validate_workflow, lint_workflow, audit_workflow |
| Run lifecycle | start_workflow, get_run_status, advance_run, advance_consensus, advance_round_robin, advance_map, resume_run, cancel_run |
| Run observability | list_runs, get_run_history |
| Verification | test_workflow |
| Skill import | list_skills, convert_skill, finalize_skill_conversion |
| Docs | list_docs, get_doc, search_docs, ask_docs, answer_docs |
Workflow CRUD#
list_workflows#
Lists all workflows from both Global and Project sources.
| Input | Type | Required |
|---|---|---|
| — | — | — |
Returns: Array<{ id, name, description: string | null, source: 'global' | 'repo', shadowed: boolean }>
When the same id exists in both sources, repo wins and the global entry has
shadowed: true. Cached agent prompts that assume single-dir lookups will
silently start picking up repo workflows once a repo source exists — pass
source: "global" to disambiguate.
get_workflow#
Returns the full workflow configuration.
| Input | Type | Required |
|---|---|---|
workflow_id |
string | yes |
source |
'global' | 'repo' |
no |
Returns: the parsed WorkflowConfig plus a source field. When source is
omitted, resolves repo-first if a project source exists.
create_workflow#
Writes a new workflow YAML file.
| Input | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | Must match [a-zA-Z0-9_-]+ |
source |
'global' | 'repo' |
no | Defaults to repo when a project source exists, else global |
config |
object | no | If omitted, writes a minimal template |
overwrite |
boolean | no | Default false. Refuses on collision when false. |
Returns: { id, source, path } on success, or { error, code, details? }.
Validates schema + integrity before writing. code values: invalid_id,
invalid_config, integrity_error, conflict, no_repo_source,
io_error.
update_workflow#
Replaces an existing workflow's contents. Validates schema + integrity; refuses to write on either failure.
| Input | Type | Required |
|---|---|---|
id |
string | yes |
source |
'global' | 'repo' |
no |
config |
object (WorkflowConfig) |
yes |
Returns: { ok: true, source, path } or { error, code, details? }.
delete_workflow#
Hard-deletes a workflow file. Requires literal confirm: true to actually
delete, and REFUSES when another workflow references this one from a
subworkflow node — returning { error, referencedBy } so you can see what
would break. Pass force: true to delete anyway.
| Input | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | |
source |
'global' | 'repo' |
no | |
confirm |
true (literal) |
yes | |
force |
boolean | no | Default false. Delete even if referenced. |
Returns: { ok: true, path, source }, { error, referencedBy } when
referenced, or { error, code }.
list_workflow_references#
List workflows that reference the given workflow through a subworkflow
node: the parents that would be orphaned by a delete, or left pointing at a
dead id by a rename without a cascade. Call before delete_workflow or
rename_workflow. The search covers every workflow on disk regardless of
source, so source scopes nothing here; it exists only for input-shape
consistency with the rest of this group.
| Input | Type | Required |
|---|---|---|
workflow_id |
string | yes |
source |
'global' | 'repo' |
no |
Returns: { referrers: Array<{ workflowId, nodeId }> } — one entry per
referencing subworkflow node.
move_workflow#
Move a workflow between Global and Project. Refuses if the target source already has the same id.
| Input | Type | Required |
|---|---|---|
id |
string | yes |
to |
'global' | 'repo' |
yes |
Returns: { ok: true, id, from, to, path } or { error, code }.
duplicate_workflow#
Copy a workflow to a new id (and optionally a new source). Updates the
new file's name field to match to_id.
| Input | Type | Required |
|---|---|---|
from_id |
string | yes |
from_source |
'global' | 'repo' |
no |
to_id |
string | yes |
to_source |
'global' | 'repo' |
yes |
overwrite |
boolean | no |
Returns: { id, source, path } or { error, code, details? }.
rename_workflow#
Rename the file stem AND update the name field on disk. Refuses if
new_id already exists in the same source.
By default the rename cascades: every workflow whose subworkflow node
points at the old id is rewritten to the new one. Pass
rewrite_references: false to rename the file alone and leave those
references pointing at an id that no longer resolves.
| Input | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | |
new_id |
string | yes | |
source |
'global' | 'repo' |
no | |
rewrite_references |
boolean | no | Default true — cascade the rename. |
Returns: { id, source, path, rewritten, failed, rolledBack } or
{ error, code, details? }. rewritten lists the workflows updated,
failed those that could not be, and rolledBack is true when a partial
failure caused the whole rename to be undone.
Authoring helpers#
get_workflow_schema#
Returns the JSON Schema for the workflow YAML, the registered executor
list (with their config keys), the model ids permitted by roscoe.yaml, and
an authoring array of runtime gotchas worth reading before you write.
Call this once before authoring a new workflow so you have the exact
shapes your write will be validated against.
| Input | Type | Required |
|---|---|---|
| — | — | — |
Returns: { jsonSchema, executors: ExecutorSummary[], models: { available, default } | { error } }.
validate_workflow#
Dry-run schema + integrity validation. No filesystem writes. Pass either
the full config OR id (+ optional source).
| Input | Type | Required (mutex) |
|---|---|---|
config |
object | one of config or id |
id |
string | one of config or id |
source |
'global' | 'repo' |
no |
Returns: { valid: true, errors: [] } or
{ valid: false, code, errors: string[] }.
lint_workflow#
Schema + integrity validation plus a non-fatal lint pass: dead states,
unreachable transitions, models missing from roscoe.yaml. Same input
shape as validate_workflow.
| Input | Type | Required (mutex) |
|---|---|---|
config |
object | one of config or id |
id |
string | one of config or id |
source |
'global' | 'repo' |
no |
Returns: { errors: string[], warnings: { code, message, state? }[] }.
Warning codes include dead_state, unreachable_state, unknown_model.
Run lifecycle#
start_workflow#
Start a workflow in MCP mode. Synchronously runs through any
script / condition nodes, then pauses at the first AI or human
node and returns a pendingStep.
| Input | Type | Required | Notes |
|---|---|---|---|
workflow_id |
string | yes | |
context |
record | no | |
source |
'global' | 'repo' |
no | |
host |
'claude_code' | 'cowork' | 'desktop_chat' |
no | Your surface. Decides whether fan-out can run on the user's subscription. |
spending_cap_usd |
number (≥ 0) | no | Run stops with stop_reason: "spending_cap_exceeded" once the total exceeds it. |
spending_cap_tokens |
integer (≥ 0) | no | Same, on total input + output tokens. |
Returns: { runId, status, url, workflow, narration, pendingStep? }
plus the AdvanceResult cost fields, and hostHint when a metered
desktop-chat caller hits a fan-out node. workflow is { id, name, description } and narration is a ready-to-send opening line naming the
workflow and its live URL. pendingStep appears for any node that hands
back — ai_agent, ai_judge, consensus, human, round_robin, map.
url points at the run page on the active server (dev port or
roscoe serve port) and is a deep link: it carries a ?project= query
(?project=global for a global: true run, otherwise the run's
projectId) so opening it auto-selects the matching workspace in the web
UI. runId is alphanumeric-only so the URL survives markdown rendering in
a chat client unescaped.
get_run_status#
Get the current status of a run, including any pending AI step.
| Input | Type | Required |
|---|---|---|
run_id |
string | yes |
Returns: the same { runId, status, url, pendingStep? } shape.
Important: if the returned status is paused and the pendingStep is
for a human node, you must NOT call resume_run autonomously — display
the prompt and validTransitions to the user as a numbered list and wait
for their reply. Only ai_agent / ai_judge pending steps may be
answered programmatically (via advance_run).
advance_run#
Provide Claude's response to a pending AI step. For ai_judge, the
response must satisfy the configured validator (the JSON envelope shape
the prompt asked for). For ai_agent, any response is accepted.
| Input | Type | Required | Notes |
|---|---|---|---|
run_id |
string | yes | |
response |
string | yes | |
usage |
object | no | Real token counts / cost: { inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens, costUsd }, all optional. Supplying it records the exact API-equivalent cost instead of estimating from text length. |
thinking |
string | no | The model's chain-of-thought for this step. Stored on the turn and shown in the run inspector's Exchange tab. |
Returns: the updated AdvanceResult shape (status, optional
pendingStep for the next pause).
advance_consensus#
Advance a consensus step that paused for a subagent-capable host (Claude
Code / Cowork): spawn agentCount independent subagents (none sees another's
answer), each on the pendingStep model and prompt (or, if the pendingStep
carries an agents array, subagent i runs agents[i].prompt on
agents[i].model), and return every raw response here.
A most-consistent (USC) node pauses twice, signalled by consensusPhase:
generate (each subagent returns free-form prose, not a JSON envelope), then
aggregate (one subagent picks the most consistent response and returns
{"reasoning": "...", "index": N}). For gate/answer nodes Roscoe validates
each verdict; if some fail and a fix could still change the outcome, the node
re-pauses with consensusPhase: 'retry' and a reduced agents[] covering
only the failed ones.
| Input | Type | Required | Notes |
|---|---|---|---|
run_id |
string | yes | |
results |
Array<{ raw: string }> |
yes (min 1) | One entry per subagent, in agents[] order when that array was given. |
Returns: the updated AdvanceResult shape.
advance_round_robin#
Advance a round_robin step, keyed by the pendingStep's roundRobinPhase.
When it's resolve (candidates need a model call), the pendingStep carries a
single agents[0].prompt: run one subagent and return its text as result.
Otherwise (first / contested) the pendingStep carries criterion,
model, and matchups (each { id, a, b }): spawn one independent subagent
per matchup, have it pick A or B (no ties) with a 0-100 decisiveness margin,
and return verdicts keyed by matchup id. Submit verdicts as waves
complete; the node re-pauses with any still-outstanding matchups (and, in
contested mode, a reversed re-judge batch) until every matchup is in.
| Input | Type | Required | Notes |
|---|---|---|---|
run_id |
string | yes | |
verdicts |
Array<{ id, winner: string, margin: number, reasoning?: string }> |
no | Matchup rounds only (first/contested). |
result |
{ raw: string } |
no | Resolve round only (roundRobinPhase: 'resolve'). |
Returns: the updated AdvanceResult shape.
advance_map#
Advance a map step. The pendingStep carries agents (one { model, prompt } per subagent), agentCount, and mapPhase. Run exactly
agentCount independent subagents on agents[i].model / agents[i].prompt
and return each raw response here, in agent order, regardless of phase:
extract (one subagent turns a free-text list into branch prompts, then the
node re-pauses with them), map (one subagent per branch; an empty response
is a failed branch), or reduce (one subagent synthesizes the branch
outputs). After the map round, a configured reduce re-pauses the node once
more if at least one branch succeeded.
| Input | Type | Required | Notes |
|---|---|---|---|
run_id |
string | yes | |
results |
Array<{ raw: string }> |
yes (min 1) | One entry per subagent, in agent order: agentCount on the map round, exactly 1 on the reduce round. |
Returns: the updated AdvanceResult shape.
resume_run#
Resume a workflow paused at a human node by supplying the chosen
transition.
| Input | Type | Required |
|---|---|---|
run_id |
string | yes |
transition |
string | yes |
Do not call autonomously. A human node represents a required human
decision point. When a run is paused at one, you must (1) display the
node's prompt, (2) present the valid transitions as a numbered list, and
(3) wait for the user to reply with their choice before calling this tool.
Do not infer or assume the choice.
Returns: the updated run detail.
cancel_run#
Cancel a running or paused workflow. In-flight executor work (a running script
worker or claude CLI/SDK call) is aborted so it stops promptly rather than
finishing in the background, and the node the run was sitting on is finalized
with the dedicated cancelled status (distinct from failed). Errors (not a
no-op) if the run is already terminal.
| Input | Type | Required |
|---|---|---|
run_id |
string | yes |
Returns: the run detail.
Run observability#
list_runs#
List recent workflow runs, newest first.
| Input | Type | Required | Notes |
|---|---|---|---|
workflow_id |
string | no | |
status |
pending|running|paused|completed|failed|cancelled |
no | |
since |
ISO 8601 datetime | no | Returns runs with startedAt >= since |
limit |
int 1..500 | no | Default 25 |
kind |
'real' | 'test' | 'all' |
no | Default 'real'. test shows ephemeral test rows |
Returns: an array of { id, workflowId, workflowName, status, startedAt, completedAt, source, workflowSource, kind }.
get_run_history#
Full per-node trace for one run, including child sub-runs (e.g. consensus agents).
| Input | Type | Required |
|---|---|---|
run_id |
string | yes |
Returns:
{
run: {
id, workflowId, workflowName, status,
startedAt, completedAt, source, workflowSource, context,
parentRunId, // the parent run, when this is a subworkflow child
childRunIds, // subworkflow runs this run spawned
awaitingChild // { childRunId } while paused on a subworkflow node
},
nodes: Array<{
id, nodeId, nodeType, status,
startedAt, completedAt, durationMs, attempts,
input, output, error,
children: Array<{ id, nodeId, nodeType, status, ... }>
}>
}Verification#
test_workflow#
Execute a workflow end-to-end against the real runner and the real DB,
then delete the run row in a finally block. Identical execution path to
production runs; the only differentiator is kind: 'test' on the
workflow_runs row, which keeps these out of user-facing run lists.
| Input | Type | Required (mutex) | Notes |
|---|---|---|---|
workflow_id |
string | one of workflow_id or config |
|
source |
'global' | 'repo' |
no | |
config |
object | one of workflow_id or config |
Inline WorkflowConfig |
context |
record | no | Same shape as start_workflow.context |
mock_responses |
Record<nodeId, string> |
no | Required for any AI/human node reached |
timeout_ms |
int 100..600000 | no | Default 60000 |
Returns (success):
{
ok: true,
finalStatus: 'completed' | 'failed' | 'cancelled',
durationMs: number,
nodeTrace: Array<{ nodeId, nodeType, status, input, output, error, children, ... }>
}Returns (failure): { ok: false, code, message, nodeId?, nodeType?, finalStatus?, durationMs?, nodeTrace?, details? }.
code values: invalid_input, workflow_not_found, invalid_config,
missing_mock, timeout, runner_error,
fanout_in_test_mode_unsupported, unexpected.
Limitations and warnings:
- Every fan-out node —
consensus,round_robin,map, andsubworkflow(whose child may itself fan out) — calls the live Anthropic API directly and cannot be mocked. Workflows containing one are rejected withfanout_in_test_mode_unsupportedunlessANTHROPIC_API_KEYis set, in which case real API spend will occur. The classification comes from the sharedfindFanoutNode, so a new fan-out node type is covered here and instart_workflow's gate from one definition — do not re-list the types by hand, which is howround_robinwas once missed. - The same rejection fires when the workflow config cannot be read.
timeout_mscannot interrupt a single non-yieldingscript— set the script's own timeout for that workload.- The runner sweeps orphaned
kind: 'test'rows on MCP startup (gated onROSCOE_SWEEP_TEST_RUNS=1, set automatically by the MCP entry point). - Mocks are required for any
ai_agent,ai_judge, orhumannode the run reaches. Missing mocks fail fast withcode: 'missing_mock'rather than hanging.
Skill import#
Convert markdown skills (the Agent Skills open standard — SKILL.md files
with name + description frontmatter, optionally bundled with helper
scripts) into Roscoe workflows. Works across Claude Code, Codex CLI,
OpenCode, Gemini CLI, Cursor, and other harnesses that follow the standard.
list_skills#
Find skill candidates by content signature across the standard skill roots
plus the current repo tree. A skill is any .md file whose YAML frontmatter
contains both name: and description: (and is not a Jekyll/Hugo blog
post).
| Input | Type | Required | Notes |
|---|---|---|---|
content_scan |
boolean | no | Default true. Set false to check only the standard harness roots and skip the repo-tree fallback. |
Scan roots checked (in order):
- User-global:
~/.claude/skills/,~/.agents/skills/,~/.codex/skills/(honors$CODEX_HOME),~/.config/opencode/skills/,~/.gemini/skills/ - Project-local (walks up to git root):
.claude/skills/,.agents/skills/,.opencode/skills/ - Fallback: the repo tree, walked to a depth of 6. It does not consult
.gitignore— the only exclusions are a fixed set:.git,node_modules,dist,build,.next,.turbo,.nx,coverage,.cache. Skipped entirely whencontent_scan: false.
Returns: { count, skills: Array<{ path, name, description }> }.
convert_skill + finalize_skill_conversion#
Convert one skill into a Roscoe workflow, in two steps so the conversion
runs on your Claude subscription (the host runs the completion) rather than
the Roscoe server calling a model. Bundled scripts/ are copied alongside the
workflow and reachable from script nodes via $ROSCOE_WORKFLOW_DIR. Out of
scope: references/, examples/, assets/ folders are detected and reported
but not bundled. For a worked walkthrough, see
Import a workflow from a skill.
Step 1 — convert_skill resolves the skill, reads its bundle, and returns
a converter prompt for you to run as your own completion. It makes no
server-side model call.
| Input | Type | Required | Notes |
|---|---|---|---|
skill_path |
string | yes | Absolute path to a skill directory or markdown file. |
target_id |
string | no | Workflow id. Defaults to the skill's name frontmatter, slug-normalized. |
target_source |
'global' | 'repo' |
no | Defaults to repo when a project source exists, otherwise global. Pass 'global' explicitly to force the user-global location. |
overwrite |
boolean | no | Default false. Refuses on id collision otherwise. |
Returns a handback: { action: 'run_completion', prompt, model, finalize_args }.
Run prompt yourself, then pass the raw output plus finalize_args to
finalize_skill_conversion. (Resolve failures return { ok: false, stage: 'resolve', message }.)
Step 2 — finalize_skill_conversion takes your completion and writes the
workflow. It re-reads the skill bundle from skill_path, so nothing you echo
back is trusted as a write source.
| Input | Type | Required | Notes |
|---|---|---|---|
raw_model_output |
string | yes | The completion you ran on convert_skill's prompt. |
skill_path |
string | yes | From finalize_args (the bundle is re-read from it). |
target_id |
string | no | From finalize_args. |
target_source |
'global' | 'repo' |
yes | From finalize_args. |
overwrite |
boolean | no | From finalize_args. Default false. |
Returns: { id, source, path, assetCount, skippedBundleDirs, skippedNote? }.
Failure shape: { ok: false, stage, message } where stage is one of
resolve, parse, validate, write. On parse/validate, the response
includes rawModelOutput so you can re-run the prompt or repair — do not
fabricate a workflow to force success.
Conversion guidance the prompt gives the model:
- Use
{{ nodeId.field }}template interpolation whenever a step references "the previous result", "the gathered context", etc. - A reference is only valid if the producing node runs on every path that
reaches the consuming node (dominance check). Branching workflows must
reference a node both branches converge through, or use a fallback like
{{ nodeId.field || "default" }}. - Default to
ai_judgefor routine routing; reach forconsensus(3-5 agents with a quorum) for high-stakes decisions (production deploys, security severity, migration safety). - Cycles (
verify → fix → re-verify) must havemaxIterationson at least one participating node, unless ahumannode sits in the loop.
Docs#
The docs tools cover three retrieval modes plus a synthesized Q&A wrapper. Pick by intent:
| Tool | Use when |
|---|---|
list_docs |
You need the catalog — slugs and titles for every page. |
get_doc |
You already know the slug and want the entire page. |
search_docs |
Shallow lookup — ranked snippets are enough. |
ask_docs |
You want full heading sections to ground your own answer (preferred for LLM hosts). |
answer_docs |
You want the retrieval packaged into a citation-required prompt to answer yourself. |
list_docs#
Returns the flat manifest [{ slug, title, description }] for every doc
page on disk.
| Input | Type | Required |
|---|---|---|
| — | — | — |
get_doc#
Fetch the full markdown body for one slug.
| Input | Type | Required | Notes |
|---|---|---|---|
slug |
string | yes | No leading slash, no .md suffix (e.g. recipes/approval-flow) |
Returns: { frontmatter, markdown } or { error }.
search_docs#
Full-text search across all docs.
| Input | Type | Required | Notes |
|---|---|---|---|
query |
string | yes | Supports prefix + fuzzy |
limit |
int 1..50 | no | Default 20 |
Returns: ranked hits, each with a headingId and a snippet of up to
~80 characters either side of the match — call get_doc (or
ask_docs for richer context) for the full body.
ask_docs#
Knowledge-base retrieval. Takes a natural-language question and returns the most relevant heading sections (the markdown from a heading down to the next equal-or-higher heading), packed within a character budget. Designed for LLM hosts that want enough grounded context to answer the question themselves.
| Input | Type | Required | Notes |
|---|---|---|---|
question |
string | yes | Natural-language question |
maxSections |
int 1..20 | no | Default 5 |
maxChars |
int 500..20000 | no | Total char budget. Default 6000. |
Returns:
{
question: string;
sections: Array<{
slug: string;
title: string;
headingId: string | null; // null = page lede
headingText: string | null;
text: string; // full section markdown (mermaid + code preserved)
score: number;
}>;
truncated: boolean; // true if more candidates were dropped to fit budget
totalChars: number;
}The headingId matches the id rehype-slug produces in the rendered DOM,
so /docs/${slug}#${headingId} is a valid deep link.
answer_docs#
Q&A packaging. Internally calls ask_docs, then returns a citation-required
prompt for you to answer yourself — it makes no server-side model call, so
the answer runs on your Claude subscription. Present your answer with the
returned citations. LLM hosts that just want the raw source should prefer
ask_docs.
| Input | Type | Required | Notes |
|---|---|---|---|
question |
string | yes | |
model |
string | no | Suggested model id for the answer. Default claude-haiku-4-5. |
maxSections |
int 1..20 | no | Default 5. Forwarded to ask_docs. |
maxChars |
int 500..20000 | no | Default 6000. Forwarded to ask_docs. |
Returns:
{
action: 'answer_prompt';
instructions: string; // answer `prompt` yourself, cite `citations`
prompt: string; // the citation-required answer prompt to run
model: string; // suggested model id
citations: Array<{
slug: string;
title: string;
headingId: string | null;
headingText: string | null;
}>;
}Failure modes:
- Empty/whitespace question → error.
- Zero relevant sections in retrieval → error (the tool refuses to hand back a
prompt with no source; fall back to
ask_docsto inspect the empty retrieval).
AdvanceResult shape#
The shared shape returned by start_workflow, get_run_status,
advance_run, advance_consensus, advance_round_robin, advance_map,
resume_run, and cancel_run:
{
runId: string;
status: 'running' | 'completed' | 'failed' | 'paused' | 'cancelled';
url: string; // deep link: /open/runs/<id>?project=<projectId|global>
// — /open hands the run to the desktop app when it is
// installed, and falls back to the browser otherwise
pendingStep?: {
runId: string;
nodeId: string;
nodeType: 'ai_agent' | 'ai_judge' | 'consensus' | 'human' | 'round_robin' | 'map';
prompt: string; // full prompt — Claude reads this, calls advance_run
validTransitions: string[];
label?: string; // the node's human-readable name
transitions?: { event: string; target: string; targetLabel?: string }[];
agentCount?: number; // consensus: how many subagents to spawn
model?: string; // consensus + round_robin: the model to use
agents?: { model: string; prompt: string }[]; // per-agent diversity
consensusPhase?: string; // generate | aggregate | retry
criterion?: string; // round_robin: what to judge on
matchups?: RoundRobinMatchup[];
roundRobinPhase?: string;
mapPhase?: string;
};
// plus the cost fields: totalCostUsd, meteredCostUsd,
// subscriptionEquivCostUsd, billingMode, inputTokens, outputTokens
}Where to next#
- Using Roscoe over MCP — the same surface as a walkthrough, including how to connect a host to it.
- CLI commands —
roscoe mcpitself, and the commands that share this server's data. - REST API — the HTTP surface behind the same runs and workflows.
- Workflow sources — what
sourceand the global-vs-repo split mean for every tool above. - Subscription vs metered — what
hostdecides, and why fan-out nodes hand back instead of calling the API.