Skip to Content
InvestigationsCasesAI agents & MCP

AI agents & MCP

Everything you do on a case — arranging its board, cleaning it up, following watches, weighing hypotheses — an AI client can do as well, over the MCP server: Claude, Cursor, Codex or your own agent. The assistant in the app uses the very same tools, and asks you before every change it makes.

An agent’s changes are ordinary board changes: they land on the timeline under mcp, other people’s open boards update, and a closed case stays read-only.


Give the client a token

Settings → MCP Server → Create token. Switch All tools off and tick what the client needs:

ScopeFor
CasesFinding cases, putting evidence in, watches, clean-up and finding rules, hypotheses, the timeline
Case BoardReading the board, notes, frames, links and stances, placing and tidying, connections, snapshots
Findings, AssetsSearching for the evidence to put on the board

A client that should only read and arrange boards still needs Cases to find a case by name (search_cases); without it, give it the case id.

Case Board is its own scope. Its tools used to be part of Cases, and tokens that had Cases were given Case Board automatically on upgrade. A token created since needs Case Board ticked.


Read the board

get_case_board returns the board as a labelled summary: every item with what it stands for, so a client never has to join ids across lists.

{
  "board": { "caseId": "3f5c…", "version": 12, "readOnly": false, "caseStatus": "OPEN" },
  "counts": { "evidence": 3, "findings": 7, "hypotheses": 1, "notes": 1, "frames": 1, "links": 1, "stances": 2, "unplaced": 1 },
  "items": [
    {
      "id": "8f0c…", "kind": "EVIDENCE", "label": "payroll-2024.xlsx", "x": 40, "y": 60, "parentId": "c2d1…",
      "assetId": "a9e2…", "findingCount": 2,
      "findings": [{ "findingId": "f-17", "type": "IBAN", "value": "AT61 1904 3002 3457 3201", "severity": "HIGH", "state": "new" }]
    },
    {
      "id": "5b7e…", "kind": "HYPOTHESIS", "label": "Payroll left via the share", "unplaced": true,
      "status": "PROPOSED", "stances": { "supports": 2, "contradicts": 0, "neutral": 0 }
    }
  ],
  "links": [], "stances": [], "relations": [{ "source": "8f0c…", "target": "a41b…" }]
}
  • A finding’s state is the one the board shows: open, new, gone, resolved, dismissed or deleted (see Evidence & findings).
  • Positions: x/y of an item with a parentId — inside a frame, or a comment pin — are relative to that parent. unplaced items have no position yet.
  • relations are the lines the platform draws between evidence (lineage, duplicates). Nobody can change those on the board.
  • board.version goes back as baseVersion when changing the board.
  • view: "full" returns the raw board instead; snapshotId reads a snapshot in the same shape.

Change the board

apply_case_board_ops takes a batch of changes, applied in order in one go. Each change succeeds or is refused on its own (rejected, with a reason), so one stale item never sinks the rest.

{
  "caseId": "3f5c…",
  "baseVersion": 12,
  "ops": [
    { "type": "item.create", "id": "0b6e2a7c-0f4e-4c2a-9a55-3c1d2e4f5a6b", "kind": "NOTE", "x": 900, "y": 40,
      "content": { "text": "Both exports carry the same IBAN. Who ran them?" }, "style": { "color": "yellow" } },
    { "type": "link.create", "id": "9d1fb3e2-7c4a-4f1e-8b2d-5e6f7a8b9c0d", "kind": "related_to", "certainty": "SUSPECTED",
      "source": { "itemId": "0b6e2a7c-0f4e-4c2a-9a55-3c1d2e4f5a6b" }, "target": { "itemId": "8f0c…", "findingId": "f-17" } },
    { "type": "stance.set", "hypothesisItemId": "5b7e…", "target": { "itemId": "8f0c…" }, "stance": "SUPPORTS", "note": "Same IBAN" }
  ]
}
On the board you…The op
Add a note or a frameitem.create (NOTE with content.text, FRAME with content.title)
Move, resize, recolour, highlight, collapse, move into a frameitem.update (x, y, width, height, parentId, style, collapsed)
Delete a note, frame, pin or hypothesis card, and undo ititem.delete, item.restore
Draw a link, edit it, delete itlink.create, link.update, link.delete / link.restore
Make a link a global relationshiplink.promote
Add evidence, remove it from the caseevidence.add (an asset, or a finding with its asset), evidence.remove
Attach or detach a findingfinding.attach, finding.detach
Add a hypothesis, set its stanceshypothesis.create, stance.set, stance.remove
Comment, resolve a commentcomment.create, comment.resolve
Put a thread back on the boardthread.place
  • Ids are the client’s: every new item or link gets a fresh UUID, so a batch sent twice changes nothing twice, and later ops of the same batch can refer to it (the link above points at the note created before it).
  • Undo is the matching restore; removed evidence comes back with its findings, notes and stances.
  • x/y can be left out when adding evidence, hypotheses or comments; then let the board place them.

Arrange the board

ToolDoesChanges what people arranged?
place_case_board_itemsGives every item without a position a spot next to what it connects to, as the board does when someone opens itNo: placed items never move
frame_case_board_itemsGroups items into a new frame (laid out anew, or kept where they stand with arrangement: "keep") or into an existing one, which grows to fitOnly the items named
tidy_case_boardTidy up: lays the whole board out again, left to right along its relations, and sends dragged findings back to their spotsYes, all of it

tidy_case_board replaces the arrangement people made. Run it with dryRun: true first to see every move, and take a snapshot first if the current layout matters. Each move reports where the item was (from), so a tidy can be walked back with item.update.

The agent’s layouts use the same sizes as the board in the browser — assets with their findings fanned out around them, cards, notes, frames — so what an agent places does not land on top of what is there.


Trace connections

trace_case_connections is Show connections for an agent: what the case’s assets connect to beyond the board, upstream (what feeds them), downstream (what they feed) and alongside (duplicates, look-alikes), hop by hop. Every node says whether it is in the case already (inCase, with its board item). An agent brings one in with evidence.add. See Connections & neighbours.


Clean-up, filters and escalation

Every rule that takes things out of a case can be previewed first, without changing anything:

PreviewThen
preview_case_cleanup: what the clean-up switches would take out nowupdate_case with removeGoneFindings, removeResolvedFindings or removeGoneAssets
preview_case_finding_filters: what filters would take out, or escalations markadd_case_finding_filters, update_case_finding_filter, remove_case_finding_filter

clear_case_escalations takes escalation marks off once they are dealt with. The rules themselves are explained in Clean-up & filters and Escalation.


Watches and hypotheses

  • Hypothesis rules: list_case_hypothesis_rules, add_case_hypothesis_rule, update_case_hypothesis_rule and remove_case_hypothesis_rule hand a watch’s answers to a hypothesis as supporting, contradicting or neutral, and land them beside it on the board. See Hypothesis rules.
  • Watches: link_case_inquiries links questions; set_case_inquiry_auto_pull turns a watch’s auto-add on or off; unlink_case_inquiry stops the case following it (its per-watch rules go with the link).
  • Verdicts: update_case_thread sets a hypothesis’s status (Supported, Refuted, …) and confidence, and writes the change in its log. add_case_thread_entry adds notes, or a new statement of the claim.
  • Stances on the board are stance.set / stance.remove.

Snapshots

take_case_board_snapshot freezes the board as it is; list_case_board_snapshots lists them (closing a case takes one by itself), and get_case_board with a snapshotId reads one back.


What stays with people

Some things are deliberately not open to agents: deleting a case, deleting a hypothesis with its whole log (an agent can take the card off the board, which keeps the thread), deleting a global relationship everywhere, and exporting the board as an image. They destroy history the case exists to keep, so they stay a person’s decision in the app.


Tool reference

Generated from the MCP server itself. Open a tool for its parameters (* = required).

Case Board case_board

The canvas a case is arranged on: read it as a labelled summary; add notes, frames, links and hypothesis stances; place new items, tidy the board up and group items into frames; trace what the evidence connects to beyond the board; and take snapshots. Pair with Cases so a client can find cases and change what they hold.

get_case_boardread-onlyRead a case's board, the canvas the case is arranged on. By default a labelled summary: every item with what it stands for — EVIDENCE (the asset, and the findings the case holds on it with their state: open, new, gone, resolved, dismissed or deleted), HYPOTHESIS (title, status, confidence, stance counts), NOTE (its text), FRAME (title, number of members) and COMMENT pins (latest text, what they are pinned to) — plus the links drawn between items or single findings, hypothesis stances, the platform relations between the evidence (lineage, duplicates: drawn by the board, not editable) and threads not on the board. x/y of an item with a parentId (inside a frame, or a comment pin) are relative to that parent; `unplaced` items have no position yet (place_case_board_items gives them one). board.version is the baseVersion for apply_case_board_ops. view "full" returns the raw board payload instead (large; the neighbourhood graph only with includeGraph). snapshotId reads a snapshot (list_case_board_snapshots) in the same shape: the board exactly as it was captured.
caseId*string (uuid)—
viewenumOne of: summary, full
includeGraphbooleanview "full" only: include the neighbourhood graph
snapshotIdstring (uuid)—
apply_case_board_opsdestructiveidempotentChange a case's board with a batch of ops, applied in order in one transaction; each op succeeds or is rejected on its own (`rejected`, with a reason and a code such as NOT_FOUND, STALE or ALREADY_ON_BOARD). Read the board first (get_case_board) for item ids, and pass its board.version as baseVersion. Ids of new things are yours: give every new item or link a fresh UUID (id / itemId), so a resent batch is harmless and later ops of the same batch can refer to it. opId is optional (op-1, op-2, … in the result). An endpoint is {itemId, findingId?}: a board item, optionally one finding of an EVIDENCE item. Ops: item.create (NOTE with content.text, FRAME with content.title), item.update (x, y, width, height, z, parentId — a FRAME to put the item in, null to take it out —, collapsed, style: color, highlight, rowHighlights per finding, findingPositions; content), item.delete and item.restore (notes, frames, comment pins and hypothesis cards: a card leaves the board, its thread stays; never evidence), link.create/update/delete/restore (kind such as related_to, same_entity, communicates_with, derived_from, contradicts, precedes or your own; certainty CONFIRMED or SUSPECTED; confidence 0–1), link.promote (copies a link between evidence into the global graph, for every case), evidence.add (an asset, or a finding, which brings its asset) and evidence.remove, finding.attach/detach, hypothesis.create (optionally with supports), stance.set/remove (SUPPORTS, CONTRADICTS, NEUTRAL), comment.create/resolve and thread.place. x/y may be left out of evidence.add, hypothesis.create, comment.create and thread.place: then call place_case_board_items to give them a spot. x/y inside a frame are relative to the frame. Every delete can be undone with the matching restore. Closed cases are read-only.
caseId*string (uuid)—
baseVersionintegerboard.version you read; defaults to the current version
ops*unknown[]—
place_case_board_itemswritesidempotentGive every item on a case's board that has no position yet — evidence, hypotheses or comments added without x/y, by an agent, a watch or a lead — a spot next to what it connects to: what the board does by itself when someone opens it, done now. Comment pins go by the item they annotate; items with nothing on the board to sit next to go into one block right of the board; a board with nothing placed gets one layered layout. Never moves an item that already has a position. Returns each placed item with its position, and the new board version.
caseId*string (uuid)—
tidy_case_boarddestructiveidempotentTidy up a case's board, like the board's own Tidy up: lay everything at the top level out again, left to right along its relations (what feeds or supports something sits left of it), each connected group together and the groups packed side by side; items in a frame move with their frame, dragged findings go back to their spots around their asset, and items without a position get one. This replaces the arrangement people made: run it with dryRun first to see the moves, prefer place_case_board_items when only new items need a spot, and take_case_board_snapshot first if the current arrangement matters. Every move says where the item was (`from`); to walk a tidy back, send those positions as item.update ops (apply_case_board_ops).
caseId*string (uuid)—
dryRunboolean—
frame_case_board_itemswritesGroup items on a case's board into a frame, a named box whose members move with it. Pass `title` (and optionally `color`) for a new frame, or `frameId` for one already on the board (a `title` then renames it). A new frame lays its members out anew inside, relations left to right, where they were and clear of everything else (arrangement "compact", the default); "keep" wraps them where they stand. Into an existing frame each item takes the next free spot and the frame grows to fit. Items in another frame move over; comment pins stay with the item they annotate. Returns the frame, where each member landed (x/y relative to the frame, and where it was), and what was skipped and why.
caseId*string (uuid)—
itemIds*string[]—
titlestring—
frameIdstring (uuid)—
colorenumOne of: yellow, blue, green, pink, gray, red, amber, violet
arrangementenumOne of: compact, keep
trace_case_connectionsread-onlyShow connections: what the assets of a case connect to beyond its board, hop by hop through the global graph — upstream (what feeds them: lineage), downstream (what they feed) and alongside (duplicates, look-alikes, drawn links). Traces from every asset in the case unless assetIds names some. kinds narrows which relations are followed; depth is hops (1–6, 6 meaning as far as it goes; default 2); limit caps the nodes returned (default 150, at most 300), and `truncated` says when it was hit. Each node says whether it is in the case already (inCase, with its board itemId), which side it is on and which node it was reached from (via). Add one to the case with apply_case_board_ops evidence.add.
caseId*string (uuid)—
assetIdsstring[]—
directionenumOne of: up, down, both
depthinteger—
kindsenum[]One of: lineage, links, duplicates, similar, meaning
limitinteger—
list_case_board_snapshotsread-onlyA case board's snapshots, newest first: frozen copies of the board taken when the case closed (reason CASE_CLOSED) or on request (MANUAL). Read one with get_case_board and its snapshotId.
caseId*string (uuid)—
take_case_board_snapshotwritesCapture a case's board as it is now in an immutable snapshot (kept for good; the case timeline records it). Take one before a big rearrangement (tidy_case_board) or before handing a case over, so the arrangement a conclusion was drawn from survives later edits.
caseId*string (uuid)—

Cases cases

Investigation cases: evidence and findings, linked questions (watches), clean-up and finding rules, hypotheses and discussion threads, and the timeline. Pair with Case Board to arrange the canvas.

search_casesread-onlySearch cases with filters and pagination.
searchstring—
statusenum[]One of: OPEN, IN_PROGRESS, CLOSED, ARCHIVED
severityenum[]One of: CRITICAL, HIGH, MEDIUM, LOW, INFO
skipinteger—
limitinteger—
get_caseread-onlyFetch a single case with evidence, findings, and linked questions.
id*string (uuid)—
create_casewritesCreate an investigation case, optionally linking questions.
title*string—
descriptionstring—
statusenumOne of: OPEN, IN_PROGRESS, CLOSED, ARCHIVED
severityenumOne of: CRITICAL, HIGH, MEDIUM, LOW, INFO
assigneestring—
createdBystring—
inquiryIdsstring[]—
removeGoneFindingsbooleanClean-up: findings the scans no longer see (retired by a run, or deleted) leave the case by themselves
removeResolvedFindingsbooleanClean-up: findings someone resolved leave the case
removeGoneAssetsbooleanClean-up: assets deleted from their source leave the case, with their findings
update_casedestructiveUpdate case metadata, status, severity, AI mode, or its clean-up switches. Switching a clean-up rule on applies it right away: what it takes out is reported in `cleanup` and written on the case timeline — run preview_case_cleanup first to see how much that is. Closing a case goes through close_case (it needs a conclusion and snapshots the board), not through status here.
id*string (uuid)—
titlestring—
descriptionstring—
statusenumOne of: OPEN, IN_PROGRESS, CLOSED, ARCHIVED
severityenumOne of: CRITICAL, HIGH, MEDIUM, LOW, INFO
assigneestring—
conclusionstring—
aiModeenumOne of: INHERIT, MANAGED, OBSERVE_ONLY
removeGoneFindingsbooleanClean-up: findings the scans no longer see (retired by a run, or deleted) leave the case by themselves
removeResolvedFindingsbooleanClean-up: findings someone resolved leave the case
removeGoneAssetsbooleanClean-up: assets deleted from their source leave the case, with their findings
close_casewritesClose a case with a conclusion. Linked questions are archived unless they drive another open case, or keepInquiries is true (use it when the questions are standing checks that should keep running after the case is answered).
id*string (uuid)—
conclusion*string—
closedBystring—
keepInquiriesboolean—
reopen_casewritesReopen a closed/archived case and reactivate the questions that were archived alongside it.
id*string (uuid)—
notestring—
reopenedBystring—
add_case_evidencewritesAttach an asset as evidence to a case.
id*string (uuid)—
entityType*stringMust be "asset"
entityId*stringAsset UUID
notestring—
addedBystring—
attach_case_findingswritesBatch-attach findings to a case by ID. Asset evidence rows are created automatically.
id*string (uuid)—
findingIds*string[]—
addedBystring—
pull_case_from_inquirywritesPull a linked question's current matches into the case as evidence and findings.
id*string (uuid)—
inquiryId*string—
findingIdsstring[]Specific finding IDs (omit = all current matches)
link_case_inquirieswritesLink additional questions to a case. Already-linked ones are ignored. Ids also named in autoPullInquiryIds will pull their new matches into the case by themselves as later scans land them.
id*string (uuid)—
inquiryIds*string[]—
autoPullInquiryIdsstring[]—
set_case_inquiry_auto_pullwritesidempotentTurn automatic pulling of a linked question's new matches into a case on or off (the watch's auto-add). On: matches that later scans land join the case by themselves, subject to the case's filters. Off: they wait for pull_case_from_inquiry — escalation rules still bring matching answers in. The question must be linked already (link_case_inquiries).
id*string (uuid)—
inquiryId*string—
autoPull*boolean—
unlink_case_inquirydestructiveUnlink a question (a watch) from a case: the case stops following it. The question itself and the evidence it already brought in stay; the filters and escalation rules scoped to that watch are dropped with the link. To end a case and archive its questions, use close_case instead.
id*string (uuid)—
inquiryId*string—
preview_case_cleanupread-onlyWhat a case's automatic clean-up would take out right now, without changing anything: findings the scans no longer see (removeGoneFindings), findings someone resolved (removeResolvedFindings), and assets deleted from their source, with their findings (removeGoneAssets). Name the switches to preview, or leave them all out to preview all three. Returns a count per switch and a sample. Switching one on (update_case) applies it at once.
id*string (uuid)—
removeGoneFindingsbooleanClean-up: findings the scans no longer see (retired by a run, or deleted) leave the case by themselves
removeResolvedFindingsbooleanClean-up: findings someone resolved leave the case
removeGoneAssetsbooleanClean-up: assets deleted from their source leave the case, with their findings
list_case_finding_filtersread-onlyA case's finding rules, case-wide (inquiryId null) or for one linked question: filters (action EXCLUDE — kinds of finding it does not want) and escalations (action ESCALATE — kinds that need attention). A FINDING_TYPE rule names a finding type exactly; a VALUE_PATTERN rule is a regular expression over the matched value. includeOptions also lists the finding types the case holds and its questions answer, which is what a FINDING_TYPE rule can pick from.
id*string (uuid)—
includeOptionsboolean—
inquiryIdstringWith includeOptions: the types of this one question
preview_case_finding_filtersread-onlyWhat unsaved finding rules would do to a case right now, without saving anything: how many findings in the case a filter (action EXCLUDE, the default) would take out or an escalation (ESCALATE) would mark — in total and per rule — with a sample, why a rule could not be saved (`problems`, per rule), and for filters how many assets would be left without a finding in the case (emptiedAssets). Same rules as add_case_finding_filters; also use it to try a new pattern before update_case_finding_filter.
id*string (uuid)—
actionenumOne of: EXCLUDE, ESCALATE
inquiryIdstring | nullA linked question to scope the rules to; omit for the whole case
rules*object[]—
add_case_finding_filtersdestructiveAdd finding rules to a case: filters (action EXCLUDE, the default) or escalations (action ESCALATE). A filter keeps a kind of finding out: matches in the case are detached right away (the timeline lists them), and pulls from the case's questions — automatic ones and pull-everything — skip them from then on; attaching one by hand still works. An escalation marks a kind of finding that needs attention: matches already in the case are marked escalated now, and a question brings in new matching answers by itself (even with auto-add off) and raises a notification; filters win over escalations. Scope: omit inquiryId for the whole case, or give a linked question to apply to its answers only. removeEmptiedAssets (filters only) also takes out every asset the filter leaves without a finding in the case. Run with dryRun first: it reports how many findings would be detached or escalated (and emptiedAssets: how many assets would be left without findings).
id*string (uuid)—
actionenumOne of: EXCLUDE, ESCALATE
inquiryIdstring | null—
rules*object[]—
removeEmptiedAssetsboolean—
dryRunboolean—
update_case_finding_filterdestructiveChange one finding rule of a case (a filter or an escalation): its pattern and/or description. A new pattern applies at once — a filter takes out the findings it now matches (removeEmptiedAssets also takes out assets left without a finding in the case), an escalation marks them — and the timeline records the change. Try the new pattern with preview_case_finding_filters first. A rule's kind, action and scope cannot change: remove it (remove_case_finding_filter) and add another.
id*string (uuid)—
filterId*string (uuid)—
patternstring—
descriptionunknown—
removeEmptiedAssetsboolean—
remove_case_finding_filterwritesRemove one finding rule (filter or escalation) from a case. Findings a filter detached stay detached, and findings an escalation marked stay marked (clear_case_escalations clears them); the questions simply stop applying the rule.
id*string (uuid)—
filterId*string (uuid)—
list_case_hypothesis_rulesread-onlyA case's hypothesis rules: what the case does, by itself, with the answers of one of its linked questions. Each rule names the question (inquiryId), the hypothesis (threadId) and a stance (SUPPORTS, CONTRADICTS or NEUTRAL), and optionally narrows to some answers (kind FINDING_TYPE with the finding type, or VALUE_PATTERN with a regular expression over the matched value; both absent = every answer). linkCount is how many links the rule made and still holds.
id*string (uuid)—
add_case_hypothesis_rulewritesMake a linked question hand its answers to a hypothesis. From then on every answer the question brings into the case (its auto-add, a pull-everything, or a person picking) is linked to the hypothesis with the stance — SUPPORTS, CONTRADICTS (a reject) or NEUTRAL — and its evidence lands on the board beside the hypothesis, inside the hypothesis's frame when it has one. A rule is the case's own: the question is untouched and other cases keep their own handling. One answer can go to several hypotheses with different stances. applyToExisting also links what the case already holds from the question; links a person made are never changed. Needs the question linked to the case and the hypothesis to be a HYPOTHESIS thread of it.
id*string (uuid)—
inquiryId*string—
threadId*string—
stanceenumOne of: SUPPORTS, CONTRADICTS, NEUTRAL
kindunknown—
patternunknown—
descriptionunknown—
applyToExistingboolean—
update_case_hypothesis_rulewritesChange a hypothesis rule's hypothesis, stance, matcher or description. A new stance or hypothesis carries the links the rule already made along (updateLinks false leaves them as they are); a new matcher only applies to answers from then on.
id*string (uuid)—
ruleId*string (uuid)—
threadIdstring—
stanceenumOne of: SUPPORTS, CONTRADICTS, NEUTRAL
kindunknown—
patternunknown—
descriptionunknown—
updateLinksboolean—
remove_case_hypothesis_ruledestructiveRemove a hypothesis rule. The links it made stay (they become the hypothesis's own links) unless removeLinks is true, which takes those links away; evidence stays in the case and on the board either way.
id*string (uuid)—
ruleId*string (uuid)—
removeLinksboolean—
clear_case_escalationswritesTake the escalation mark off findings of a case once a person has dealt with them (all escalated findings when findingIds is omitted). The findings stay in the case; the timeline records the clearing.
id*string (uuid)—
findingIdsstring[]Finding ids (not case-finding ids); omit for all
get_case_graphread-onlyGet the evidence neighbourhood graph for a case.
id*string (uuid)—
depthinteger—
get_case_timelineread-onlyPaginated unified case activity feed (newest first). Narrow it with `types` (e.g. FINDINGS_ESCALATED, FINDINGS_AUTO_REMOVED) or to one linked watch with `inquiryId`.
caseId*string (uuid)—
cursorstring—
limitinteger—
typesenum[]Only these activity types One of: CASE_CREATED, CASE_UPDATED, CONCLUSION_UPDATED, INQUIRY_LINKED, INQUIRY_UNLINKED, INQUIRY_PULLED, EVIDENCE_ADDED, EVIDENCE_REMOVED, EVIDENCE_NOTE_UPDATED, FINDING_ADDED, FINDING_REMOVED, FINDING_NOTE_UPDATED, THREAD_CREATED, THREAD_ENTRY_ADDED, THREAD_STATEMENT_UPDATED, THREAD_STATUS_CHANGED, THREAD_CONFIDENCE_CHANGED, SUPPORT_LINKED, SUPPORT_UNLINKED, SUPPORT_UPDATED, LEAD_PROPOSED, LEAD_ACCEPTED, LEAD_DISMISSED, EVENT_ADDED, EVENT_UPDATED, EVENT_REMOVED, BOARD_NOTE_ADDED, BOARD_NOTE_UPDATED, BOARD_NOTE_REMOVED, BOARD_FRAME_ADDED, BOARD_FRAME_UPDATED, BOARD_FRAME_REMOVED, BOARD_LINK_ADDED, BOARD_LINK_UPDATED, BOARD_LINK_REMOVED, BOARD_LINK_PROMOTED, BOARD_ITEM_HIGHLIGHTED, BOARD_ARRANGED, BOARD_SNAPSHOT_TAKEN, COMMENT_RESOLVED, CLEANUP_SETTINGS_UPDATED, FINDING_FILTER_ADDED, FINDING_FILTER_UPDATED, FINDING_FILTER_REMOVED, FINDINGS_AUTO_REMOVED, EVIDENCE_AUTO_REMOVED, INQUIRY_SETTINGS_UPDATED, FINDINGS_ESCALATED, ESCALATION_CLEARED, LEADS_GENERATED, BOARD_TERM_PLACED, BOARD_TERM_REMOVED, MEANING_LINKED, MEANING_UNLINKED, HYPOTHESIS_RULE_ADDED, HYPOTHESIS_RULE_UPDATED, HYPOTHESIS_RULE_REMOVED, FINDINGS_AUTO_LINKED, THREAD_EVIDENCE_REMOVED
inquiryIdstring (uuid)—
list_case_threadsread-onlyList threads (hypothesis + discussion) for a case.
caseId*string (uuid)—
create_case_threadwritesCreate a thread on a case: a HYPOTHESIS (with status/confidence) or a DISCUSSION.
caseId*string (uuid)—
kindenumDefaults to HYPOTHESIS One of: HYPOTHESIS, DISCUSSION
title*stringHypothesis name or discussion topic
statementstringInitial statement body (hypothesis threads)
statusenumOne of: PROPOSED, SUPPORTED, REFUTED, INCONCLUSIVE
confidencenumber—
createdBystring—
add_case_thread_entrywritesAdd an entry to a thread's log. NOTE is commentary and leaves the title alone; STATEMENT revises the claim itself, and the thread title becomes the first 200 characters of its body. STATUS_CHANGE and CONFIDENCE_CHANGE entries only record a remark — they do not change the thread: use update_case_thread to change a hypothesis's status or confidence (it logs the change itself).
threadId*string (uuid)—
entryType*enumOne of: NOTE, STATEMENT, STATUS_CHANGE, CONFIDENCE_CHANGE
bodystring—
authorstring—
update_case_threadwritesidempotentUpdate a hypothesis or discussion thread: its title, its colour on the board, its verdict (status PROPOSED, SUPPORTED, REFUTED or INCONCLUSIVE), its confidence (0–1) or its testable predicate. A status or confidence change is written into the thread's log (STATUS_CHANGE / CONFIDENCE_CHANGE) and on the case timeline, so the evolution of a hypothesis stays on record. To revise the claim itself, add a STATEMENT entry (add_case_thread_entry) instead.
threadId*string (uuid)—
titlestring—
statusenumOne of: PROPOSED, SUPPORTED, REFUTED, INCONCLUSIVE
confidencenumber—
colorunknownA CSS colour such as #3b82f6; null for the default
testablePredicateunknown—
link_case_thread_supportwritesLink evidence or a finding to a thread as supporting/contradicting/neutral.
threadId*string (uuid)—
targetType*enumOne of: evidence, finding
targetId*string—
stanceenumOne of: SUPPORTS, CONTRADICTS, NEUTRAL
weightnumber—
notestring—
Last updated on