MCP tools
Four tools, one per decision the pipeline needs from a model, and the phase machine that sequences them.
The MCP server exposes four tools. Each corresponds to a point where the pipeline needs a judgement it cannot make itself.
The server retrieves, expands, counts coverage, shortlists, verifies and emits. It never writes queries, never scores a paper and never composes prose. You supply every judgement, using the skill’s rubrics.
The response envelope
Every call returns the same shape:
{ "scan_id": "…", "phase": "screen", "status": "ok", "next_action": "screen_candidates", "progress": { }, "payload": { }}Follow next_action on every response. It tells you what the pipeline needs
next, and the payload carries what you need to decide it.
next_action | What to send back |
|---|---|
screen_candidates | scan_continue with screen_scores for the batch in the payload. |
write_gap_queries | scan_continue with gap_queries. An empty list skips the gap round. |
rank_shortlist | scan_continue with ranked_entries for the page in the payload. |
verify_ranked | scan_verify. |
complete | scan_result. |
Phases are screen, gap, rank and complete.
Never issue two mutating calls for the same scan_id at once. A call that
arrives while another is running is answered in_progress — do not resubmit;
poll scan_result, which is read-only and never blocked.
scan_start
Open a scan and run retrieval. Returns the first screening batch.
| Parameter | Type | Notes |
|---|---|---|
scan_id | string | You generate it: a fresh canonical lowercase UUIDv4, one per brief. |
brief | string | The project brief in Markdown, or the question. |
queries | QueryPlan | The full query plan you wrote under the plan rubric. The server never writes queries for you. |
profile | quick/standard/deep | Default standard. |
top | int | Papers to emit. Default 10. |
foundational | int | Out-of-window slots within top. Default 2. |
since, until | string | Window bounds, YYYY-MM. |
slug | string | Run name. |
max_candidates, per_query | int | Override the profile’s depth and cap. |
force_gap_round | bool | Run the gap round whatever the profile says. |
Retrieval is the longest call in a scan. If it fails or times out, call it again
with the same scan_id and the same arguments — the server resumes the run it
already has instead of starting a second one.
The same arguments means the same. A changed brief or plan under an id already in
use is refused with scan_id_conflict rather than silently applied, and the error
names which fields differ.
scan_continue
Submit the artifact the current phase asked for, and get the next decision.
| Parameter | Type | When |
|---|---|---|
scan_id | string | Always. |
screen_scores | list | next_action is screen_candidates. Score every item in the batch. |
gap_queries | list | next_action is write_gap_queries. |
ranked_entries | list | next_action is rank_shortlist. |
Send exactly one of the three. Send none of them to poll the current phase.
An empty gap_queries list is a meaningful answer: it means you found nothing
worth reformulating, and the gap round is skipped.
The shortlist is handed to you a page at a time; the payload carries the records for the page you are on.
scan_verify
Verify every ranked paper against the live record, then emit the evidence packet.
| Parameter | Type | Notes |
|---|---|---|
scan_id | string | Always. |
ranked_entries | list | Optional. Any final entries you still hold are merged first. |
Call it once next_action is verify_ranked.
Verification is the engine’s. DOIs and titles are checked, never repaired — the same rule that governs the CLI stage.
scan_result
Read a scan’s result. Read-only, never blocked, safe to poll while a call is running.
Takes scan_id only.
On a finished scan the payload carries the evidence packets’ summary rows, the
unverified list, the counts, coverage.json and the rendered evidence.md.
Because it is read-only, it is also the correct way to check on a scan whose mutating call is still in flight.
The narrative parts of a report — why these papers, what the coverage risks are — are yours to write. The server does not compose prose.
Where runs live
The adapter keeps one directory per scan_id under
$RESEARCH_SCAN_MCP_DATA, defaulting to
~/.local/share/research-scan-mcp/runs/. Inside it is an ordinary run directory
with the same files a CLI-driven scan produces, so everything in
File formats applies unchanged.
The adapter drives the CLI as a subprocess and reads its exit codes. It never imports a stage’s internals, and it makes no judgement about papers.
Errors
Failures come back as a refusal you can act on, carrying a reason code — a bad artifact, the wrong phase for the call, or a stage that failed — with the CLI’s own structured error rather than a stack trace.
scan_id_conflict is the one worth handling explicitly: it means the id is in use
with different inputs. Use a fresh id for a different brief.
Last updated Aug 21, 2026