MCP reference

Heartwood's local MCP server: every action the app can take, exposed as tools your AI assistant can call — loopback-only, on your own machine.

Writing a standalone script or tool instead of extending an AI assistant? The same actions are also exposed as a documented local HTTP API — see the API reference.

Connect your AI

Heartwood's local MCP server is reachable over two transports — pick whichever your client supports. Both are loopback-only: nothing leaves your machine except to the model you personally chose.

stdio (recommended — no port or token to manage)

Your client launches heartwood mcp as a subprocess; it auto-starts the daemon if needed and forwards stdio transparently.

{
  "mcpServers": {
    "heartwood": {
      "command": "heartwood",
      "args": ["mcp"]
    }
  }
}
ClientWhere this config goes
Claude Desktopclaude_desktop_config.json (Settings → Developer → Edit Config) — or use the in-app “Connect” button, which writes this for you.
Claude Codeproject-local .mcp.json, or run claude mcp add heartwood -- heartwood mcp
Cursorproject-local .cursor/mcp.json, or global ~/.cursor/mcp.json

Streamable-HTTP (loopback)

The daemon binds 127.0.0.1:0 (an OS-assigned port, never fixed) and writes a discovery file, discovery.json, inside its data directory, carrying the port and a bearer token:

{
  "port": 51234,
  "token": "…"
}

Point your client at http://127.0.0.1:<port>/mcp with that discovered token as a bearer credential. (The daemon also mints a separate human-only credential for the app's own UI — it is never documented as a client credential and will not authenticate an MCP call; the discovery-file token above is the only one your AI assistant ever needs.)

{
  "mcpServers": {
    "heartwood": {
      "url": "http://127.0.0.1:<port>/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

Non-technical walkthrough (one paste, no understanding required): Connect your AI.

Tools

Trees

tree.list

List the trees known to this daemon (id, name, created-at, archived flag). Archived trees are omitted unless `include_archived` is set.

Parameters

  • include_archived boolean

Output shape

  • active_tree_idrequired string

  • itemsrequired

    array of

    TreeView

    • archivedrequired boolean

    • created_at_msrequired integer (uint64)

    • idrequired string

    • namerequired string

Example prompts

  • “What trees exist in this daemon right now?”
  • “List all my trees, including archived ones.”

People

person.create

Conclude a new person over a set of personas — the identity conclusion that turns raw evidence into a named individual on the tree. A persona already concluded over by another live person is refused (cite proof.argument.record's output as the optional proof_argument). Human actors apply directly; agent actors always file a proposal for human approval — minting an identity is human judgment.

Parameters

  • certaintyrequired string [enum: proved, probable, possible, disproved]

  • personasrequired array of string (uuid)

  • proof_argument optional string (uuid)

  • tree_id string

Output shape

  • appliedrequired boolean

  • messagerequired string

  • person_id optional string (uuid)

  • proposal_id optional string (uuid)

Example prompts

  • “I've got a persona from the 1900 census that isn't linked to anyone yet — conclude it as a new person, Arthur Fernwood.”
  • “Turn this unmatched census entry into a real person in the tree so I can start attaching relatives to her.”

person.living_status.set

Set an explicit living/deceased override for a person, overriding the automatic likely-living guess. This gates whether the person is suppressed from a GEDCOM (family file) export by default — set it whenever the automatic guess is wrong. use_heuristic clears the override and resumes the automatic guess.

Parameters

  • personrequired string (uuid)

  • statusrequired string [enum: living, deceased, use_heuristic]

  • tree_id string

Output shape

  • okrequired boolean

Example prompts

  • “Mark Arthur as deceased so he stops being suppressed from the family file I export.”
  • “I don't actually know whether this person is still living — clear my earlier override and go back to the automatic guess.”

person.merge

Merge two persons into one: concludes a new person over the union of both persons' personas, then retracts both originals. Refused if the two persons share a persona. Reversible with person.split using the two original persona sets, which the merge event itself records.

Parameters

  • leftrequired string (uuid)

  • rightrequired string (uuid)

  • tree_id string

Output shape

  • person_idrequired string (uuid)

Example prompts

  • “I think 'Arthur Fernwood' and 'Art Fernwood' are the same man entered twice — merge them into one person.”
  • “These two entries are clearly duplicates from two different census years. Combine them so the tree only shows one person.”

person.retract

Undo a mistaken person.create: removes the person's conclusion entirely (state-removal, not a status flag). The underlying personas are untouched — they remain available to conclude over again with person.create.

Parameters

  • personrequired string (uuid)

  • tree_id string

Output shape

  • okrequired boolean

Example prompts

  • “I created a person by mistake while testing — undo that, but keep whatever source appearances I'd already linked to her.”
  • “That last person I added was a typo, not a real conclusion. Take it back.”

person.split

Split a person into two, partitioning its persona set across caller-supplied left/right groups (every persona must land in exactly one side). This is how a merge is undone, and also how a mistaken person.create over unrelated personas is corrected.

Parameters

  • leftrequired array of string (uuid)

  • personrequired string (uuid)

  • rightrequired array of string (uuid)

  • tree_id string

Output shape

  • leftrequired string (uuid)

  • rightrequired string (uuid)

Example prompts

  • “I merged two people together and it turns out they were actually different individuals — split them back apart.”
  • “This person record is conflating two different Arthur Fernwoods. Separate the census appearances back into the two original people.”

persona.create

Mint a new persona against an existing source — the raw evidence-layer appearance ("John Smith, age 34" as this record names them), not yet an identity conclusion. Plain write, ungated for both human and agent actors: minting a persona commits to nothing about who the person is. Use person.create afterward to conclude an identity over one or more personas. `label` is the record's own wording and nothing parses a name out of it: pass `name` (with `research_context`) to state the given/surname pieces. Without it the person concluded here has no surname at all — none is guessed from the label.

Parameters

  • labelrequired string

  • name

    optional
    • display optional string

    • given array of string

    • surname array of string

  • research_context

    optional
    • search_contextrequired string

    • sessionrequired string (uuid)

  • sourcerequired string (uuid)

  • tree_id string

Output shape

  • persona_idrequired string (uuid)

Example prompts

  • “This 1900 census page names a household member who isn't in the tree at all yet — mint a persona for her: Clara Fernwood, age 8.”
  • “Create a new persona against this source for the witness named in the marriage record, before I try to conclude who they are.”

Relationships

relationship.attach

Attach a parent or spouse edge between two persons. `kind` is "parent" (relative becomes person's parent), "child" (relative becomes person's child — the reversed edge), or "spouse" (person and relative become spouses). Refuses self-relationships and ancestry cycles before writing anything. Idempotent: attaching an edge that already exists returns it unchanged with `created: false`. Requires research_context, same as assertion.capture.

Parameters

  • kindrequired string

  • personrequired string (uuid)

  • relativerequired string (uuid)

  • research_contextrequired

    • search_contextrequired string

    • sessionrequired string (uuid)

  • sourcerequired string (uuid)

  • tree_id string

Output shape

  • assertionsrequired array of string (uuid)

  • createdrequired boolean

  • eventrequired string (uuid)

Example prompts

  • “The 1900 census names John Doe as Mary Doe's father — attach that parent relationship.”
  • “Mark these two as spouses based on the marriage record I just added.”
  • “Add Jane as a child of Robert — I have the birth certificate as the source.”

relationship.reparent

Replace one of a child's parents with a different parent, in place — the displaced participation is superseded by the replacement, so the correction stays auditable. Refuses ancestry cycles before writing anything. Requires research_context, same as assertion.capture.

Parameters

  • childrequired string (uuid)

  • from_parentrequired string (uuid)

  • research_contextrequired

    • search_contextrequired string

    • sessionrequired string (uuid)

  • sourcerequired string (uuid)

  • to_parentrequired string (uuid)

  • tree_id string

Output shape

  • assertionsrequired array of string (uuid)

  • createdrequired boolean

  • eventrequired string (uuid)

Example prompts

  • “The census I sourced this from actually names a different father — replace him with the correct one.”
  • “I attached the wrong mother earlier; swap her for the one this new record names.”

Undo

edit.undo

Undo one of your own edits. Pass `event` to target exactly that event, or omit it to undo your most recent executable edit (the same one edit.undo_preview's `target` reports). Undo always unwinds the calling agent's own writes only — it can never undo a human's edits, and a second call with nothing of your own left to undo is refused, not a silent no-op. The compensating event (e.g. a refutation) is returned, never applied silently.

Parameters

  • event optional string (uuid)

  • tree_id string

Output shape

  • compensatingrequired array of string (uuid)

  • undonerequired string (uuid)

  • undone_event_typerequired string

Example prompts

  • “That last detail you recorded was wrong — take it back.”
  • “Undo your most recent change to this tree.”
  • “You attached that fact to the wrong person; undo it and we'll re-enter it correctly.”

edit.undo_preview

Report undo status without undoing anything: the literal last edit you (this agent) authored, with its disposition (undoable now, already undone, deferred, or not undoable — each with why), and what a no-target edit.undo call would actually undo. Scope is always your own edits — never a human's.

Parameters

  • tree_id string

Output shape

  • last_edit

    optional

    EditDispositionView

    • eventrequired string (uuid)

    • event_typerequired string

    • undoablerequired

      UndoableNowView

      • issue optional string

      • rationale optional string

      • staterequired string

  • target

    optional

    UndoTargetView

    • at_msrequired integer (uint64)

    • eventrequired string (uuid)

    • event_typerequired string

Example prompts

  • “Before you undo anything, show me what your last change was and whether it can be taken back.”
  • “What would an undo revert right now?”

Sessions & assignments

assignment.get

Read a recorded assignment by id: what prompt (and arguments) it was recorded from, and when. Assignments are recorded automatically the first time this connection successfully calls get_prompt — there is no tool to create one directly.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • contentrequired

    AssignmentContentView (tagged union)

    kind = raw

    • kindrequired string [const: raw]

    • textrequired string

    kind = structured

    • argumentsrequired map: string → string

    • kindrequired string [const: structured]

    • prompt_namerequired string

  • idrequired string (uuid)

  • recorded_at_msrequired integer (uint64)

Example prompts

  • “What prompt and arguments was this assignment recorded from?”
  • “Look up assignment {id} — what was I actually asked to do?”

session.current

Read the calling connection's own current session, if it has minted one yet (no args). Reports null before this connection's first write — never a synthesized session.

Parameters

  • tree_id string

Output shape

  • actor_model optional string

  • actor_reported_harness optional string

  • current_assignment optional string (uuid)

  • event_countrequired integer (uint)

  • first_seen_at_msrequired integer (uint64)

  • last_seen_at_msrequired integer (uint64)

  • sessionrequired string (uuid)

Example prompts

  • “What's my current session, if I have one yet?”
  • “Have I minted a store session on this connection?”

session.get

Read a store session's summary by id: which agent model wrote under it, its declared harness, first/last-seen time, how many events it wrote, and its current assignment (if any). A session is minted server-side the first time a connection writes.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • actor_model optional string

  • actor_reported_harness optional string

  • current_assignment optional string (uuid)

  • event_countrequired integer (uint)

  • first_seen_at_msrequired integer (uint64)

  • last_seen_at_msrequired integer (uint64)

  • sessionrequired string (uuid)

Example prompts

  • “Who was writing under this session, and how many events did they record?”
  • “Look up session {id} — what model and harness did it declare, and does it have a current assignment?”

Evidence capture

assertion.capture

Capture a new assertion (a claim about a persona, backed by a source). `research_context` is required, not optional metadata: the research log is a byproduct of every capture, per the GPS's reasonably-exhaustive-search component. Agent-origin assertions are always captured in the pending lane (epistemic status `pending confirmation`) — never immediately usable as fact until a human confirms them.

Parameters

  • attached

    array of

    kind = external_record

    • idrequired

      kind = ancestry_record

      • dbidrequired string

      • kindrequired string [const: ancestry_record]

      • recordrequired string

      kind = doi

      • doirequired string

      • kindrequired string [const: doi]

      kind = family_search_ark

      • arkrequired string

      • kindrequired string [const: family_search_ark]

      kind = permalink

      • kindrequired string [const: permalink]

      • urlrequired string

    • kindrequired string [const: external_record]

    kind = offline_record

    • citationrequired string

    • kindrequired string [const: offline_record]

    kind = source_blob

    • blobrequired string

    • kindrequired string [const: source_blob]

  • claimrequired string

  • evidence_class

    optional
    • directnessrequired string [enum: direct, indirect]

    • informantrequired string [enum: primary, secondary]

    • originalityrequired string [enum: original, derivative]

  • kindrequired

    kind = fact

    • kindrequired string [const: fact]

    kind = transcription_claim

    • kindrequired string [const: transcription_claim]

    • of_blobrequired string

    • region

      optional
      • heightrequired number (double)

      • widthrequired number (double)

      • xrequired number (double)

      • yrequired number (double)

  • personarequired string (uuid)

  • research_contextrequired

    • search_contextrequired string

    • sessionrequired string (uuid)

  • sourcerequired string (uuid)

  • tree_id string

Output shape

  • claimrequired string

  • epistemic_statusrequired string

  • event_context

    optional

    EventContextOutput

    • date

      optional

      GenealogicalDateInput (tagged union)

      kind = about

      kind = after

      kind = before

      kind = between

      • kindrequired string [const: between]

      • valuerequired array of any

      kind = exact

      kind = range

      • kindrequired string [const: range]

      • valuerequired array of any

    • event_idrequired string (uuid)

    • kindrequired

      LifeEventKindInput

      • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

      • phrase string

    • kind_phrase optional string

    • place

      optional

      PlaceInput

      • authority_id optional string

      • coordinates

        optional

        CoordinatesInput

        • latitude_nanodegrequired integer (int64)

        • longitude_nanodegrequired integer (int64)

      • jurisdictionsrequired array of string

      • valid_time_note optional string

    • sibling_participationsrequired

      array of

      SiblingParticipationOutput

      • persona_idrequired string (uuid)

      • persona_label_idrequired string

      • rolerequired

        ParticipantRoleInput

        • phrase string

        • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

      • statusrequired string

    • source_idrequired string (uuid)

  • evidence_class

    optional

    EvidenceClassInput

    • directnessrequired string [enum: direct, indirect]

    • informantrequired string [enum: primary, secondary]

    • originalityrequired string [enum: original, derivative]

  • idrequired string (uuid)

  • next_steprequired string

  • personarequired string (uuid)

  • sourcerequired string (uuid)

Example prompts

  • “Capture that Arthur Fernwood was born about 1870 in Elsewhere County, citing the 1900 census I just found.”
  • “Add an assertion for Beatrice's death date from the obituary — remember, it needs a real source and research context, not just my hunch.”

assertion.evidence.attach

Attach a piece of corroborating evidence (a source blob or an external record id) to an existing assertion. Strengthens — never substitutes for — human confirmation.

Parameters

  • assertionrequired string (uuid)

  • evidencerequired

    kind = external_record

    • idrequired

      kind = ancestry_record

      • dbidrequired string

      • kindrequired string [const: ancestry_record]

      • recordrequired string

      kind = doi

      • doirequired string

      • kindrequired string [const: doi]

      kind = family_search_ark

      • arkrequired string

      • kindrequired string [const: family_search_ark]

      kind = permalink

      • kindrequired string [const: permalink]

      • urlrequired string

    • kindrequired string [const: external_record]

    kind = offline_record

    • citationrequired string

    • kindrequired string [const: offline_record]

    kind = source_blob

    • blobrequired string

    • kindrequired string [const: source_blob]

  • tree_id string

Output shape

  • claimrequired string

  • epistemic_statusrequired string

  • event_context

    optional

    EventContextOutput

    • date

      optional

      GenealogicalDateInput (tagged union)

      kind = about

      kind = after

      kind = before

      kind = between

      • kindrequired string [const: between]

      • valuerequired array of any

      kind = exact

      kind = range

      • kindrequired string [const: range]

      • valuerequired array of any

    • event_idrequired string (uuid)

    • kindrequired

      LifeEventKindInput

      • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

      • phrase string

    • kind_phrase optional string

    • place

      optional

      PlaceInput

      • authority_id optional string

      • coordinates

        optional

        CoordinatesInput

        • latitude_nanodegrequired integer (int64)

        • longitude_nanodegrequired integer (int64)

      • jurisdictionsrequired array of string

      • valid_time_note optional string

    • sibling_participationsrequired

      array of

      SiblingParticipationOutput

      • persona_idrequired string (uuid)

      • persona_label_idrequired string

      • rolerequired

        ParticipantRoleInput

        • phrase string

        • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

      • statusrequired string

    • source_idrequired string (uuid)

  • evidence_class

    optional

    EvidenceClassInput

    • directnessrequired string [enum: direct, indirect]

    • informantrequired string [enum: primary, secondary]

    • originalityrequired string [enum: original, derivative]

  • idrequired string (uuid)

  • next_steprequired string

  • personarequired string (uuid)

  • sourcerequired string (uuid)

Example prompts

  • “Attach this second census image as corroborating evidence for the birth-date assertion I already captured.”
  • “Add the FamilySearch record id as supporting evidence on this claim, alongside the source blob I already cited.”

assertion.supersede

Correct a claim by superseding it with a replacement assertion, rather than editing it in place — the original stays in the record, marked superseded, and the replacement takes its place. Refused if the original is already refuted, superseded, or still awaiting human confirmation. This does NOT resolve a conflict: a contradiction is settled only by a proof argument, so any conflict the superseded claim stood in stays open and comes back in `conflicts_still_open`.

Parameters

  • assertionrequired string (uuid)

  • byrequired string (uuid)

  • tree_id string

Output shape

  • conflicts_still_openrequired array of string (uuid)

  • okrequired boolean

Example prompts

  • “I found a better source for her birth date — supersede the old claim with the one I just captured, rather than editing it.”
  • “That transcription was wrong. Replace it with the corrected claim I just logged, and leave the original marked as superseded.”

association.record

Record an eventless typed association between two personas (e.g. godparent, neighbor, employer) — no shared life event required. Requires research_context, same as assertion.capture.

Parameters

  • attached

    array of

    kind = external_record

    • idrequired

      kind = ancestry_record

      • dbidrequired string

      • kindrequired string [const: ancestry_record]

      • recordrequired string

      kind = doi

      • doirequired string

      • kindrequired string [const: doi]

      kind = family_search_ark

      • arkrequired string

      • kindrequired string [const: family_search_ark]

      kind = permalink

      • kindrequired string [const: permalink]

      • urlrequired string

    • kindrequired string [const: external_record]

    kind = offline_record

    • citationrequired string

    • kindrequired string [const: offline_record]

    kind = source_blob

    • blobrequired string

    • kindrequired string [const: source_blob]

  • evidence_class

    optional
    • directnessrequired string [enum: direct, indirect]

    • informantrequired string [enum: primary, secondary]

    • originalityrequired string [enum: original, derivative]

  • otherrequired string (uuid)

  • personarequired string (uuid)

  • relationrequired

    relation = employer

    • relationrequired string [const: employer]

    relation = friend_or_associate

    • relationrequired string [const: friend_or_associate]

    relation = godparent

    • relationrequired string [const: godparent]

    relation = neighbor

    • relationrequired string [const: neighbor]

    relation = other

    • phraserequired string

    • relationrequired string [const: other]

  • research_contextrequired

    • search_contextrequired string

    • sessionrequired string (uuid)

  • sourcerequired string (uuid)

  • tree_id string

Output shape

  • assertion_idrequired string (uuid)

Example prompts

  • “Record that Bertram Cole was Arthur Fernwood's neighbor, per the 1878 town directory.”
  • “Note that this minister witnessed the marriage — record him as an associate, even though there's no shared event for it yet.”

event.correct

Propose a correction to a life event's date, place, or kind — field-discriminated by the `field` parameter ("date" | "place" | "kind"). This tool always files a Proposal; corrections filed by an agent never apply directly, only a human approving the proposal executes them. Requires a source and evidence, since a corrected value must stay citation-carrying.

Parameters

  • date

    optional

    kind = about

    kind = after

    kind = before

    kind = between

    • kindrequired string [const: between]

    • valuerequired array of any

    kind = exact

    kind = range

    • kindrequired string [const: range]

    • valuerequired array of any

  • eventrequired string (uuid)

  • evidence

    array of

    kind = external_record

    • idrequired

      kind = ancestry_record

      • dbidrequired string

      • kindrequired string [const: ancestry_record]

      • recordrequired string

      kind = doi

      • doirequired string

      • kindrequired string [const: doi]

      kind = family_search_ark

      • arkrequired string

      • kindrequired string [const: family_search_ark]

      kind = permalink

      • kindrequired string [const: permalink]

      • urlrequired string

    • kindrequired string [const: external_record]

    kind = offline_record

    • citationrequired string

    • kindrequired string [const: offline_record]

    kind = source_blob

    • blobrequired string

    • kindrequired string [const: source_blob]

  • fieldrequired string

  • kind

    optional
    • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

    • phrase string

  • kind_phrase optional string

  • place

    optional
    • authority_id optional string

    • coordinates

      optional
      • latitude_nanodegrequired integer (int64)

      • longitude_nanodegrequired integer (int64)

    • jurisdictionsrequired array of string

    • valid_time_note optional string

  • source_idrequired string (uuid)

  • tree_id string

Output shape

  • appliedrequired boolean

  • messagerequired string

  • proposal_id optional string (uuid)

Example prompts

  • “The marriage date I recorded is wrong — propose a correction with the deed I just found as the source.”
  • “Fix the place on this census life event, it should say Franklin County, not Franklin Township — cite the record.”

event.get

Read a single life event by id: its anchor fields (kind, date, place, source) plus every participation recorded against it, live or not, each with its own epistemic status.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • date

    optional

    GenealogicalDateInput (tagged union)

    kind = about

    kind = after

    kind = before

    kind = between

    • kindrequired string [const: between]

    • valuerequired array of any

    kind = exact

    kind = range

    • kindrequired string [const: range]

    • valuerequired array of any

  • date_phrase optional string

  • idrequired string (uuid)

  • kindrequired

    LifeEventKindInput

    • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

    • phrase string

  • kind_phrase optional string

  • participationsrequired

    array of

    EventParticipantViewOutput

    • assertionrequired string (uuid)

    • personarequired string (uuid)

    • persona_labelrequired string

    • rolerequired

      ParticipantRoleInput

      • phrase string

      • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

    • statusrequired string

  • place

    optional

    PlaceInput

    • authority_id optional string

    • coordinates

      optional

      CoordinatesInput

      • latitude_nanodegrequired integer (int64)

      • longitude_nanodegrequired integer (int64)

    • jurisdictionsrequired array of string

    • valid_time_note optional string

  • sourcerequired string (uuid)

  • source_titlerequired string

Example prompts

  • “Show me everyone who's recorded as a participant in this 1900 census household event.”
  • “Pull up the full detail on this marriage event, including everyone's role and epistemic status.”

event.participant.add

Add one more participation to an EXISTING life event (a persona playing a role in an anchor already recorded). An exact live duplicate (same persona, role, source) is a no-op returning the existing participation id — `created: false` in the result tells you which happened.

Parameters

  • attached

    array of

    kind = external_record

    • idrequired

      kind = ancestry_record

      • dbidrequired string

      • kindrequired string [const: ancestry_record]

      • recordrequired string

      kind = doi

      • doirequired string

      • kindrequired string [const: doi]

      kind = family_search_ark

      • arkrequired string

      • kindrequired string [const: family_search_ark]

      kind = permalink

      • kindrequired string [const: permalink]

      • urlrequired string

    • kindrequired string [const: external_record]

    kind = offline_record

    • citationrequired string

    • kindrequired string [const: offline_record]

    kind = source_blob

    • blobrequired string

    • kindrequired string [const: source_blob]

  • eventrequired string (uuid)

  • evidence_class

    optional
    • directnessrequired string [enum: direct, indirect]

    • informantrequired string [enum: primary, secondary]

    • originalityrequired string [enum: original, derivative]

  • participant_rolerequired

    • phrase string

    • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

  • personarequired string (uuid)

  • research_contextrequired

    • search_contextrequired string

    • sessionrequired string (uuid)

  • sourcerequired string (uuid)

  • tree_id string

Output shape

  • createdrequired boolean

  • participation_idrequired string (uuid)

Example prompts

  • “I missed a household member — add Ida Fernwood as a daughter participant on the census event I already recorded.”
  • “Add the officiant as a participant on this marriage event; I forgot him the first time.”

event.record

Record a new life event (birth, marriage, census, etc.) as a shared anchor plus one or more participations — one participation per persona named in it, each carrying its own role. Requires research_context (the log is a byproduct, same as assertion.capture). Every participation this tool records is agent-origin: it lands pending confirmation, never immediately usable as fact.

Parameters

  • date

    optional

    kind = about

    kind = after

    kind = before

    kind = between

    • kindrequired string [const: between]

    • valuerequired array of any

    kind = exact

    kind = range

    • kindrequired string [const: range]

    • valuerequired array of any

  • kindrequired

    • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

    • phrase string

  • kind_phrase optional string

  • participantsrequired

    array of
    • attached

      array of

      kind = external_record

      • idrequired

        kind = ancestry_record

        • dbidrequired string

        • kindrequired string [const: ancestry_record]

        • recordrequired string

        kind = doi

        • doirequired string

        • kindrequired string [const: doi]

        kind = family_search_ark

        • arkrequired string

        • kindrequired string [const: family_search_ark]

        kind = permalink

        • kindrequired string [const: permalink]

        • urlrequired string

      • kindrequired string [const: external_record]

      kind = offline_record

      • citationrequired string

      • kindrequired string [const: offline_record]

      kind = source_blob

      • blobrequired string

      • kindrequired string [const: source_blob]

    • evidence_class

      optional
      • directnessrequired string [enum: direct, indirect]

      • informantrequired string [enum: primary, secondary]

      • originalityrequired string [enum: original, derivative]

    • personarequired string (uuid)

    • rolerequired

      • phrase string

      • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

  • place

    optional
    • authority_id optional string

    • coordinates

      optional
      • latitude_nanodegrequired integer (int64)

      • longitude_nanodegrequired integer (int64)

    • jurisdictionsrequired array of string

    • valid_time_note optional string

  • research_contextrequired

    • search_contextrequired string

    • sessionrequired string (uuid)

  • sourcerequired string (uuid)

  • tree_id string

Output shape

  • event_idrequired string (uuid)

  • participation_idsrequired array of string (uuid)

Example prompts

  • “Record this 1900 census household as a life event, with Arthur, Martha, and Henry as participants.”
  • “Log a marriage event for these two personas, citing the church register I just transcribed.”

note.add

Attach a free-text note to zero or more anchors (persona, person, source, assertion, life event, or citation). Notes are annotation, not evidence — no research_context is required or accepted.

Parameters

  • anchors

    array of

    kind = assertion

    • idrequired string (uuid)

    • kindrequired string [const: assertion]

    kind = citation

    • idrequired string (uuid)

    • kindrequired string [const: citation]

    kind = life_event

    • idrequired string (uuid)

    • kindrequired string [const: life_event]

    kind = person

    • idrequired string (uuid)

    • kindrequired string [const: person]

    kind = persona

    • idrequired string (uuid)

    • kindrequired string [const: persona]

    kind = source

    • idrequired string (uuid)

    • kindrequired string [const: source]

  • citations array of string (uuid)

  • textrequired string

  • tree_id string

Output shape

  • note_idrequired string (uuid)

Example prompts

  • “Add a note to this source reminding me that the microfilm quality was poor and some names are hard to read.”
  • “Attach a note to this persona saying I still need to verify the spelling of the surname.”

persona.name_pieces.set

Set structured name pieces (given, surname, prefixes, suffixes, nickname, verbatim display text) for a persona. An idempotent replace: any existing live name pieces from the same source are superseded. Requires research_context — a name is a claim about what the source says.

Parameters

  • personarequired string (uuid)

  • piecesrequired

    • displayrequired string

    • given array of string

    • name_type

      optional

      type = aka

      • typerequired string [const: aka]

      type = birth

      • typerequired string [const: birth]

      type = immigrant

      • typerequired string [const: immigrant]

      type = maiden

      • typerequired string [const: maiden]

      type = married

      • typerequired string [const: married]

      type = other

      • phraserequired string

      • typerequired string [const: other]

      type = professional

      • typerequired string [const: professional]

    • nickname array of string

    • prefix array of string

    • suffix array of string

    • surname array of string

    • surname_prefix array of string

  • research_contextrequired

    • search_contextrequired string

    • sessionrequired string (uuid)

  • tree_id string

Output shape

  • setrequired boolean

Example prompts

  • “Set the structured name pieces for this persona from the census: given name Arthur, surname Fernwood.”
  • “Record the given name, surname, and suffix as they appear verbatim in this source, replacing what's there now.”

transcription.claim

Record a transcription claim: what you read a specific source image as saying, distinct from the image itself and refutable like any other claim. Requires research_context, exactly like assertion.capture, since a transcription claim is a research act too.

Parameters

  • claimrequired string

  • of_blobrequired string

  • personarequired string (uuid)

  • region

    optional
    • heightrequired number (double)

    • widthrequired number (double)

    • xrequired number (double)

    • yrequired number (double)

  • research_contextrequired

    • search_contextrequired string

    • sessionrequired string (uuid)

  • sourcerequired string (uuid)

  • tree_id string

Output shape

  • claimrequired string

  • epistemic_statusrequired string

  • event_context

    optional

    EventContextOutput

    • date

      optional

      GenealogicalDateInput (tagged union)

      kind = about

      kind = after

      kind = before

      kind = between

      • kindrequired string [const: between]

      • valuerequired array of any

      kind = exact

      kind = range

      • kindrequired string [const: range]

      • valuerequired array of any

    • event_idrequired string (uuid)

    • kindrequired

      LifeEventKindInput

      • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

      • phrase string

    • kind_phrase optional string

    • place

      optional

      PlaceInput

      • authority_id optional string

      • coordinates

        optional

        CoordinatesInput

        • latitude_nanodegrequired integer (int64)

        • longitude_nanodegrequired integer (int64)

      • jurisdictionsrequired array of string

      • valid_time_note optional string

    • sibling_participationsrequired

      array of

      SiblingParticipationOutput

      • persona_idrequired string (uuid)

      • persona_label_idrequired string

      • rolerequired

        ParticipantRoleInput

        • phrase string

        • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

      • statusrequired string

    • source_idrequired string (uuid)

  • evidence_class

    optional

    EvidenceClassInput

    • directnessrequired string [enum: direct, indirect]

    • informantrequired string [enum: primary, secondary]

    • originalityrequired string [enum: original, derivative]

  • idrequired string (uuid)

  • next_steprequired string

  • personarequired string (uuid)

  • sourcerequired string (uuid)

Example prompts

  • “I've read the handwriting on this record image — log what it says as a transcription claim, tied to that image.”
  • “Record my transcription of this ship manifest so someone can check it against the original scan later.”

Sources & citations

citation.create

Create a citation from typed elements you supply directly (author, title, publication, locator, and so on) — the general-purpose path, for a source that doesn't fit one of the registered citation templates.

Parameters

  • elementsrequired

    array of

    kind = access_date

    • kindrequired string [const: access_date]

    • valuerequired string

    kind = author

    • kindrequired string [const: author]

    • valuerequired string

    kind = free_text

    • kindrequired string [const: free_text]

    • valuerequired string

    kind = identifier

    • idrequired

      kind = ancestry_record

      • dbidrequired string

      • kindrequired string [const: ancestry_record]

      • recordrequired string

      kind = doi

      • doirequired string

      • kindrequired string [const: doi]

      kind = family_search_ark

      • arkrequired string

      • kindrequired string [const: family_search_ark]

      kind = permalink

      • kindrequired string [const: permalink]

      • urlrequired string

    • kindrequired string [const: identifier]

    kind = locator

    • kindrequired string [const: locator]

    • valuerequired string

    kind = other

    • kindrequired string [const: other]

    • labelrequired string

    • valuerequired string

    kind = publication

    • kindrequired string [const: publication]

    • valuerequired string

    kind = repository

    • kindrequired string [const: repository]

    • valuerequired string

    kind = source_type

    • kindrequired string [const: source_type]

    • valuerequired string

    kind = title

    • kindrequired string [const: title]

    • valuerequired string

  • tree_id string

Output shape

  • citation_idrequired string (uuid)

Example prompts

  • “Build a citation for this source by hand — here's the author, title, and where I found it.”
  • “This record doesn't fit any of the citation templates. Create a citation from the elements I give you directly.”

citation.elements.set

Replace a citation's current element set with a new revision (full replacement, not a merge). Editing elements on shared evidence mutates every attached use, so this is GatedWrite: a human actor applies directly and gets the new render back; an agent actor only ever files a Proposal for a human to approve.

Parameters

  • citation_idrequired string (uuid)

  • elementsrequired map: string → string

  • tree_id string

Output shape

  • appliedrequired boolean

  • messagerequired string

  • proposal_id optional string (uuid)

  • rendered

    optional

    CitationRenderOutput

  • revision optional integer (uint32)

Example prompts

  • “Update the citation elements on this source now that I found the correct enumeration district.”
  • “Replace the film number on this citation — I had the wrong one recorded.”

citation.render

Render a citation's three Chicago-style forms (first footnote, subsequent short form, bibliography) plus a CSL-JSON interchange object. Pass either citation_id (a stored citation's current elements) or template_id + elements (an unsaved draft — the live-preview arm). Missing required fields are a structured error, not a degraded render.

Parameters

  • citation_id optional string (uuid)

  • elements map: string → string

  • form string [enum: first, subsequent, bibliography, all]

  • template_id optional string

  • tree_id string

Output shape

Example prompts

  • “Show me the Chicago-style footnote and bibliography entry for this citation.”
  • “Give me a live preview of how this census template would render with the field values I'm about to enter.”

citation.source_type.set

Record what kind of record a source is by attaching a citation template id to an existing citation — the only way an imported source reaches a template, since import never infers one. Adds the classification and changes nothing else: every existing element survives verbatim, including ones the chosen template has no field for. Re-classifying replaces the type. Missing required fields are reported, not enforced — labelling is not authoring. GatedWrite: a human actor applies directly; an agent actor only ever files a Proposal for a human to approve.

Parameters

  • citation_idrequired string (uuid)

  • template_idrequired string

  • tree_id string

Output shape

  • appliedrequired boolean

  • messagerequired string

  • missing_required optional array of string

  • proposal_id optional string (uuid)

  • revision optional integer (uint32)

  • template_id optional string

Example prompts

  • “This source came in from a GEDCOM import with no citation template — classify it as a census population schedule so it can render proper Chicago-style prose.”
  • “I mis-clicked earlier and classified this source as a will. Reclassify it as a probate file instead.”

citation.templates.list

List the 30 built-in Chicago-style citation templates (optionally filtered by category), each with its field list. Render strings are not included — call citation.render to see formatted output; agents author elements, they don't reimplement rendering.

Parameters

  • category optional string

  • tree_id string

Output shape

  • registry_versionrequired integer (uint32)

  • templatesrequired

    array of

    CitationTemplateSummary

    • categoryrequired string

    • fieldsrequired

      array of

      CitationTemplateFieldSummary

      • elementrequired string

      • idrequired string

      • labelrequired string

      • requiredrequired boolean

    • idrequired string

    • labelrequired string

Example prompts

  • “What citation templates are available for census records?”
  • “List every built-in citation template so I can find the right one for a church baptism record.”

source.attach_blob

Attach an existing content-addressed blob to a source that already exists. Same posture as assertion.evidence.attach — a direct write for both human and agent actors.

Parameters

  • blobrequired string

  • sourcerequired string (uuid)

  • tree_id string

Output shape

  • blobsrequired array of string

  • derived_from optional string (uuid)

  • idrequired string (uuid)

  • titlerequired string

Example prompts

  • “I already stored the second page of this census image as a blob — attach it to the source record too.”
  • “Attach the higher-resolution scan I just uploaded to this existing source, alongside the one already there.”

source.cited_by

What rests on this source: the claims that cite it, each with the persona it was captured against and — once that persona has been folded into an identity conclusion — the person it belongs to. The inverse of assertion.get's source field, and the only way to answer "what would I lose if this source turned out to be wrong?". Named rows, so prefer this over assertion.list's `source` filter unless you need to narrow by kind or epistemic state. Paginated (default 50, max 500 — echo next_cursor back as cursor); `total` reports the whole count behind the page, and matches the `claim_count` source.list reports for the same source.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • source_idrequired string (uuid)

  • tree_id string

Output shape

  • claimsrequired

    array of

    SourceCitedClaimView

    • assertionrequired string (uuid)

    • claimrequired string

    • epistemic_statusrequired string

    • kindrequired string

    • person optional string (uuid)

    • person_name optional string

    • personarequired string (uuid)

    • persona_labelrequired string

  • next_cursor optional string

  • totalrequired integer (uint)

Example prompts

  • “What claims in my tree rest on this marriage record?”
  • “If this source turned out to be wrong, which facts would I lose?”
  • “List everyone whose evidence comes from the GEDCOM I imported.”

source.create

Create a new Source and its Citation together from a chosen template and its field values, in one atomic step. Same posture as add_source/create_citation — a direct write for both human and agent actors, since this is source infrastructure, not evidence capture.

Parameters

  • elementsrequired map: string → string

  • template_idrequired string

  • title optional string

  • tree_id string

Output shape

  • citation_idrequired string (uuid)

  • createdrequired boolean

  • renderedrequired

    CitationRenderOutput

  • source_idrequired string (uuid)

Example prompts

  • “Create a source and citation together for this 1900 census record using the census template I found.”
  • “Set up a new source from the church register template with the field values I just transcribed.”

Research sessions & log

research.log.capture

Record a search-context log entry in one action (D1) — the design law's flagship verb. Auto-links to the given source/persona/plan item, auto-opens or reuses the active research session, and never requires a form. `found_nothing` marks a negative search; formalize it into NegativeEvidence later with research.log.negative.record.

Parameters

  • found_nothing boolean

  • persona optional string (uuid)

  • question optional string (uuid)

  • search_contextrequired string

  • session optional string (uuid)

  • source optional string (uuid)

  • tree_id string

Output shape

  • linkedrequired

    ResearchLogCaptureLinkedOutput

    • persona optional string (uuid)

    • plan_item optional string (uuid)

    • source optional string (uuid)

  • log_entry_idrequired string (uuid)

  • session_idrequired string (uuid)

Example prompts

  • “Log that I searched the county marriage index for this persona and found nothing — mark it as a negative search.”
  • “One-step log: I checked FamilySearch for this source and it confirmed the household composition.”

research.log.detail.append

Append a detail line to an existing research-log entry without mutating the original entry (AC1) — use this for context added after the fact (a page number found later, a correction to what was searched).

Parameters

  • detailrequired string

  • log_entryrequired string (uuid)

  • tree_id string

Output shape

  • log_entry_idrequired string (uuid)

Example prompts

  • “I found the exact page number after the fact — append that detail to the log entry I already made.”
  • “Add a correction detail to my earlier log entry: it was actually enumeration district 42, not 41.”

research.log.entry.add

Record a search/source context entry in the research log for a session. The research log is what a human later checks to judge whether a search was reasonably exhaustive (GPS component 2) — junk filler is visible, not hidden.

Parameters

  • search_contextrequired string

  • sessionrequired string (uuid)

  • tree_id string

Output shape

  • log_entry_idrequired string (uuid)

Example prompts

  • “Log that I checked the Ohio county marriage index for John Smith and found nothing.”
  • “Record in the research log that I searched FamilySearch's 1900 census collection for this household.”

research.log.entry.retract

Retract a research-log entry — removes it from every active-view read (research.log.list, the plan/log page) without deleting it from the log. Use this for a duplicate or wrongly-recorded entry. There is no un-retract: record a fresh entry to recover.

Parameters

  • log_entryrequired string (uuid)

  • tree_id string

Output shape

  • okrequired boolean

Example prompts

  • “I logged the same search twice — retract the duplicate entry.”
  • “Retract that log entry, I recorded it against the wrong research question.”

research.log.entry.text.set

Correct a research-log entry's search context. Records a full-replacement revision — the original entry stays in the log; every read shows the latest revision.

Parameters

  • log_entryrequired string (uuid)

  • search_contextrequired string

  • tree_id string

Output shape

  • okrequired boolean

Example prompts

  • “Correct the search context on that last log entry — I searched the state index, not the county one.”
  • “That log entry says the wrong parish; change it to St. Brigid's.”

research.log.get

Read a single research-log entry by id: its search context, attestation, timestamp, the question it belongs to, whether it recorded a null result, and every detail ever appended to it (uncapped — research.log.list caps inline details per row). The read half of research.log.detail.append, which previously had no route or verb to read details back through.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • attestationrequired string

  • detailsrequired

    array of

    LogDetailView

    • attestationrequired string

    • detailrequired string

    • idrequired string (uuid)

    • recorded_atrequired integer (int64)

  • found_nothingrequired boolean

  • idrequired string (uuid)

  • persona optional string (uuid)

  • plan_item optional string (uuid)

  • question optional string (uuid)

  • recorded_atrequired integer (int64)

  • search_contextrequired string

  • sessionrequired string (uuid)

  • source optional string (uuid)

Example prompts

  • “Show me the full record for this log entry, including every detail anyone's appended to it.”
  • “What does this log entry say — the original search context plus any follow-up notes?”

research.log.list

List research log entries, filterable by question, session, persona, or source, each with its durable source/persona/plan-item link. Complements research.question.log.get, which lists raw search-context strings for a question without link info.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • persona optional string (uuid)

  • question optional string (uuid)

  • session optional string (uuid)

  • source optional string (uuid)

  • tree_id string

Output shape

  • entriesrequired

    array of

    ResearchLogListEntryView

    • attestationrequired string

    • detailsrequired

      array of

      LogDetailView

      • attestationrequired string

      • detailrequired string

      • idrequired string (uuid)

      • recorded_atrequired integer (int64)

    • found_nothingrequired boolean

    • idrequired string (uuid)

    • persona optional string (uuid)

    • plan_item optional string (uuid)

    • question optional string (uuid)

    • recorded_atrequired integer (int64)

    • search_contextrequired string

    • sessionrequired string (uuid)

    • source optional string (uuid)

  • next_cursor optional string

  • totalrequired integer (uint)

Example prompts

  • “List every research-log entry linked to this persona.”
  • “Show me all the log entries tied to this source across every session.”

research.log.negative.record

Formalize a found-nothing log entry into NegativeEvidence (D6): records what was expected to be found, wasn't, and what that absence implies. Capture the null search first with research.log.capture {found_nothing: true}; call this afterward when ready to reason about the absence.

Parameters

  • expectationrequired string

  • inferencerequired string

  • log_entryrequired string (uuid)

  • personarequired string (uuid)

  • tree_id string

Output shape

  • negative_evidence_idrequired string (uuid)

Example prompts

  • “Formalize that failed marriage-record search into negative evidence — what I expected to find and didn't.”
  • “I already logged the null search; now record it as formal negative evidence with what its absence implies.”

research.question.close

Close a research question once its line of inquiry is settled. Optionally record a disposition (answered | abandoned | superseded | merged) and a reason; future session briefs surface both as the retired lead's record. Does not delete anything — the question and its log remain.

Parameters

  • disposition optional string [enum: answered, abandoned, superseded, merged]

  • idrequired string (uuid)

  • reason optional string

  • tree_id string

Output shape

  • closedrequired boolean

Example prompts

  • “Close the research question about Arthur's birthplace — we settled it.”
  • “This line of inquiry into the missing marriage record is a dead end for now, close it out.”

research.question.log.get

Read the full research log for a research question, across all its sessions — the record a human checks for reasonably-exhaustive-search.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • questionrequired string (uuid)

  • tree_id string

Output shape

  • entriesrequired

    array of

    LogEntryView

    • attestationrequired string

    • idrequired string (uuid)

    • search_contextrequired string

    • sessionrequired string (uuid)

  • next_cursor optional string

Example prompts

  • “Show me the full research log for this question, across every session.”
  • “What have I already searched for this research question? I don't want to duplicate work.”

research.question.open

Open a new research question — the GPS-reasoning unit of work. Every capture and log entry should trace back to a question; open one before starting a research session.

Parameters

  • questionrequired string

  • tree_id string

Output shape

  • research_question_idrequired string (uuid)

Example prompts

  • “Open a research question for Arthur Fernwood's birth date and place.”
  • “Start a new line of inquiry: who were John Smith's parents?”

research.session.end

End a research session. Does not close the parent research question — a question may span many sessions.

Parameters

  • sessionrequired string (uuid)

  • tree_id string

Output shape

  • endedrequired boolean

Example prompts

  • “I'm done researching for today — end this research session.”
  • “Wrap up this session; I'll pick the question back up tomorrow.”

research.session.start

Start a research session under an existing research question. Sessions group the search/source log entries that justify a reasonably-exhaustive-search claim.

Parameters

  • questionrequired string (uuid)

  • tree_id string

Output shape

  • session_idrequired string (uuid)

Example prompts

  • “Start a research session under this question so we can log what we find.”
  • “Begin a new session for today's research on Arthur's parents.”

Research planning & GPS coverage

research.plan.get

Read a research question's plan: its ordered candidate-source items (with done state and auto-linked log-entry counts) plus the D2 GPS checklist. The plan items ARE the minimal to-dos — there is no separate task system.

Parameters

  • questionrequired string (uuid)

  • tree_id string

Output shape

  • coveragerequired

    ChecklistViewOutput

    • analysis_correlationrequired boolean

    • complete_citationsrequired boolean

    • conflict_resolutionrequired boolean

    • exhaustive_searchrequired

      ExhaustiveSearchStatusOutput (tagged union)

      status = claimable

      • statusrequired string [const: claimable]

      status = in_progress

      • coveredrequired integer (uint32)

      • statusrequired string [const: in_progress]

      • totalrequired integer (uint32)

      status = not_started

      • statusrequired string [const: not_started]

    • written_conclusionrequired boolean

  • itemsrequired

    array of

    PlanItemView

    • descriptionrequired string

    • donerequired boolean

    • idrequired string (uuid)

    • log_entry_countrequired integer (uint32)

    • positionrequired integer (uint32)

    • source_id optional string (uuid)

Example prompts

  • “Show me the research plan for this question — what sources are still on the to-do list?”
  • “Pull up the plan and coverage checklist for this research question.”

research.plan.item.add

Add a candidate-source item to a research question's plan (an ordered to-do naming a source to check, optionally resolved to a stored SourceId). Appended at the end by default.

Parameters

  • descriptionrequired string

  • position optional integer (uint32)

  • questionrequired string (uuid)

  • source_id optional string (uuid)

  • tree_id string

Output shape

  • item_idrequired string (uuid)

Example prompts

  • “Add 'check the 1910 census' as the next item on this question's research plan.”
  • “I want to check the church baptism register next — add it to the plan.”

research.plan.item.description.set

Correct a research-plan item's description (e.g. the wrong repository was named). Records a full-replacement revision, same shape as research.question.text.set.

Parameters

  • descriptionrequired string

  • itemrequired string (uuid)

  • tree_id string

Output shape

  • okrequired boolean

Example prompts

  • “Fix the third plan item — it should say the 1880 federal census, not the 1870.”
  • “Reword this plan item to name the specific repository I need to visit.”

research.plan.item.done.set

Mark a research-plan item done or not-done (D3): an idempotent, reversible workflow flag, not an epistemic claim — it contributes to coverage display but never to an exhaustive-search claim on its own (only a human coverage confirmation does).

Parameters

  • donerequired boolean

  • itemrequired string (uuid)

  • tree_id string

Output shape

  • donerequired boolean

  • itemrequired string (uuid)

Example prompts

  • “Mark the 1900 census plan item as done — I already checked it.”
  • “I haven't actually looked at that source yet, mark the plan item as not-done.”

research.plan.item.remove

Remove a research-plan item. Does not affect any log entries already linked to it.

Parameters

  • itemrequired string (uuid)

  • tree_id string

Output shape

  • okrequired boolean

Example prompts

  • “Remove that plan item, the source turned out not to exist for this county.”
  • “Take the duplicate plan item off this question's plan.”

research.plan.item.reorder

Move a research-plan item to a new position in its question's ordered plan.

Parameters

  • itemrequired string (uuid)

  • positionrequired integer (uint32)

  • tree_id string

Output shape

  • okrequired boolean

Example prompts

  • “Move the census plan item to the top of the list, I want to check it first.”
  • “Reorder the plan so the church register comes before the newspaper archive.”

research.question.coverage.get

Read a research question's GPS checklist (D2): the five elements of a proof-standard argument, including the three-state exhaustive-search status. A pure projection — agent-reported-only logs can never reach `claimable` (the domain's own honesty rule).

Parameters

  • questionrequired string (uuid)

  • tree_id string

Output shape

  • gpsrequired

    ChecklistViewOutput

    • analysis_correlationrequired boolean

    • complete_citationsrequired boolean

    • conflict_resolutionrequired boolean

    • exhaustive_searchrequired

      ExhaustiveSearchStatusOutput (tagged union)

      status = claimable

      • statusrequired string [const: claimable]

      status = in_progress

      • coveredrequired integer (uint32)

      • statusrequired string [const: in_progress]

      • totalrequired integer (uint32)

      status = not_started

      • statusrequired string [const: not_started]

    • written_conclusionrequired boolean

Example prompts

  • “Show me the GPS checklist for this research question — how close are we to a claimable exhaustive search?”
  • “What's the current coverage status on this question's proof-standard checklist?”

research.question.get

Read a single research question by id.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • idrequired string (uuid)

  • questionrequired string

  • statusrequired string

  • subjectsrequired

    array of

    SubjectRefInput (tagged union)

    kind = life_event

    • idrequired string (uuid)

    • kindrequired string [const: life_event]

    kind = person

    • idrequired string (uuid)

    • kindrequired string [const: person]

    kind = persona

    • idrequired string (uuid)

    • kindrequired string [const: persona]

Example prompts

  • “Pull up the details on the research question about Arthur's parents.”
  • “Show me the current status of this research question.”

research.question.list

List research questions, optionally filtered by status (open/closed) or a linked subject (persona/person/life event). Each result includes its current subjects and status.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • status optional string [enum: open, closed]

  • subject

    optional

    kind = life_event

    • idrequired string (uuid)

    • kindrequired string [const: life_event]

    kind = person

    • idrequired string (uuid)

    • kindrequired string [const: person]

    kind = persona

    • idrequired string (uuid)

    • kindrequired string [const: persona]

  • tree_id string

Output shape

  • itemsrequired

    array of

    QuestionView

    • idrequired string (uuid)

    • questionrequired string

    • statusrequired string

    • subjectsrequired

      array of

      SubjectRefInput (tagged union)

      kind = life_event

      • idrequired string (uuid)

      • kindrequired string [const: life_event]

      kind = person

      • idrequired string (uuid)

      • kindrequired string [const: person]

      kind = persona

      • idrequired string (uuid)

      • kindrequired string [const: persona]

  • next_cursor optional string

Example prompts

  • “List every open research question about this persona.”
  • “Show me all closed research questions so I can review what's been settled.”

research.question.subjects.set

Replace a research question's linked subjects with the given set (diff-applied: links what's missing, unlinks what's no longer present). Subjects are personas, concluded persons, or life events — there is no Family entity (CR-3).

Parameters

  • questionrequired string (uuid)

  • subjectsrequired

    array of

    kind = life_event

    • idrequired string (uuid)

    • kindrequired string [const: life_event]

    kind = person

    • idrequired string (uuid)

    • kindrequired string [const: person]

    kind = persona

    • idrequired string (uuid)

    • kindrequired string [const: persona]

  • tree_id string

Output shape

  • questionrequired

    QuestionView

    • idrequired string (uuid)

    • questionrequired string

    • statusrequired string

    • subjectsrequired

      array of

      SubjectRefInput (tagged union)

      kind = life_event

      • idrequired string (uuid)

      • kindrequired string [const: life_event]

      kind = person

      • idrequired string (uuid)

      • kindrequired string [const: person]

      kind = persona

      • idrequired string (uuid)

      • kindrequired string [const: persona]

Example prompts

  • “Link this research question to both Arthur and his father — replace whatever subjects are on it now.”
  • “Update this question's subjects to include the newly discovered sibling.”

research.question.text.set

Correct a research question's own text (e.g. a typo). Records a full-replacement revision — the original wording stays in the event log; every read shows the latest revision.

Parameters

  • questionrequired string (uuid)

  • textrequired string

  • tree_id string

Output shape

  • okrequired boolean

Example prompts

  • “I typed this research question wrong — change it to "Who were Mary Ellen Doyle's parents, and where was she born?"”
  • “Reword the open question on this persona so it names the county, not just the state.”

Review & pending lane

assertion.get

Read a single assertion by id, with its evidence class and epistemic status.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • claimrequired string

  • epistemic_statusrequired string

  • event_context

    optional

    EventContextOutput

    • date

      optional

      GenealogicalDateInput (tagged union)

      kind = about

      kind = after

      kind = before

      kind = between

      • kindrequired string [const: between]

      • valuerequired array of any

      kind = exact

      kind = range

      • kindrequired string [const: range]

      • valuerequired array of any

    • event_idrequired string (uuid)

    • kindrequired

      LifeEventKindInput

      • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

      • phrase string

    • kind_phrase optional string

    • place

      optional

      PlaceInput

      • authority_id optional string

      • coordinates

        optional

        CoordinatesInput

        • latitude_nanodegrequired integer (int64)

        • longitude_nanodegrequired integer (int64)

      • jurisdictionsrequired array of string

      • valid_time_note optional string

    • sibling_participationsrequired

      array of

      SiblingParticipationOutput

      • persona_idrequired string (uuid)

      • persona_label_idrequired string

      • rolerequired

        ParticipantRoleInput

        • phrase string

        • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

      • statusrequired string

    • source_idrequired string (uuid)

  • evidence_class

    optional

    EvidenceClassInput

    • directnessrequired string [enum: direct, indirect]

    • informantrequired string [enum: primary, secondary]

    • originalityrequired string [enum: original, derivative]

  • idrequired string (uuid)

  • next_steprequired string

  • personarequired string (uuid)

  • sourcerequired string (uuid)

Example prompts

  • “Pull up the details on this assertion, including its epistemic status.”
  • “Is this claim confirmed or still pending? Show me the full assertion record.”

assertion.pending.list

List all agent-origin assertions currently awaiting human confirmation (the pending lane). Each result explains its epistemic status and the next step needed before it can support a proof argument.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • tree_id string

Output shape

  • epistemic_statusrequired string

  • itemsrequired

    array of

    PendingAssertionView

    • claimrequired string

    • event_context

      optional

      EventContextOutput

      • date

        optional

        GenealogicalDateInput (tagged union)

        kind = about

        kind = after

        kind = before

        kind = between

        • kindrequired string [const: between]

        • valuerequired array of any

        kind = exact

        kind = range

        • kindrequired string [const: range]

        • valuerequired array of any

      • event_idrequired string (uuid)

      • kindrequired

        LifeEventKindInput

        • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

        • phrase string

      • kind_phrase optional string

      • place

        optional

        PlaceInput

        • authority_id optional string

        • coordinates

          optional

          CoordinatesInput

          • latitude_nanodegrequired integer (int64)

          • longitude_nanodegrequired integer (int64)

        • jurisdictionsrequired array of string

        • valid_time_note optional string

      • sibling_participationsrequired

        array of

        SiblingParticipationOutput

        • persona_idrequired string (uuid)

        • persona_label_idrequired string

        • rolerequired

          ParticipantRoleInput

          • phrase string

          • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

        • statusrequired string

      • source_idrequired string (uuid)

    • evidence_class

      optional

      EvidenceClassInput

      • directnessrequired string [enum: direct, indirect]

      • informantrequired string [enum: primary, secondary]

      • originalityrequired string [enum: original, derivative]

    • idrequired string (uuid)

    • personarequired string (uuid)

    • sourcerequired string (uuid)

  • next_cursor optional string

  • next_steprequired string

  • staterequired string

Example prompts

  • “What claims are you waiting on me to confirm right now?”
  • “Show me everything in the pending lane so I can review it before my next session.”

proposal.list

List all proposed operations (redactions, exports) awaiting human disposition. Proposals are never auto-executed — a human must approve or deny each one.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • tree_id string

Output shape

  • itemsrequired

    array of

    ProposalView

    • idrequired string (uuid)

    • op_kindrequired string

    • statusrequired string

  • next_cursor optional string

  • next_steprequired string

Example prompts

  • “What proposals are waiting on my approval?”
  • “Show me every pending redaction or export request so I can decide on them.”

Proof arguments

conflict.resolve

Settle one conflict by naming the proof argument that concludes it — the only mechanism that closes a contradiction here. The argument must already exist — the id proof.argument.record returns, which for an agent caller is the id its proposal yields once a human approves it; competing claims are never merged and never silently picked between, so superseding a claim does not close its conflict. A conflict is settled once: resolving one already settled is an error, and resolving one leaves every other conflict open. Reports how many conflicts remain open afterwards, read back off the tree.

Parameters

  • conflictrequired string (uuid)

  • proof_argumentrequired string (uuid)

  • tree_id string

Output shape

  • conflictrequired string (uuid)

  • open_conflicts_remainingrequired integer (uint)

  • proof_argumentrequired string (uuid)

Example prompts

  • “Close out that birth-date conflict using the proof argument we just wrote.”
  • “Settle this contradiction — the argument concluding the 1870 date is the one to cite.”
  • “Mark the conflict on Temperance Wynwood as resolved by argument 019fd3de.”

proof.argument.export

Export a recorded proof argument as a versioned heartwood.proof_argument.v1 document: question, section-by-section body (evidence summary, conflict resolution, conclusion), citations re-rendered live via citation.render (never a frozen string), and a snapshot of its GPS checklist. This is the pinned interchange contract, not a formatted report.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • certaintyrequired string

  • claims_exhaustive_searchrequired boolean

  • gps_checklistrequired

    ChecklistViewOutput

    • analysis_correlationrequired boolean

    • complete_citationsrequired boolean

    • conflict_resolutionrequired boolean

    • exhaustive_searchrequired

      ExhaustiveSearchStatusOutput (tagged union)

      status = claimable

      • statusrequired string [const: claimable]

      status = in_progress

      • coveredrequired integer (uint32)

      • statusrequired string [const: in_progress]

      • totalrequired integer (uint32)

      status = not_started

      • statusrequired string [const: not_started]

    • written_conclusionrequired boolean

  • idrequired string (uuid)

  • provenancerequired

    ExportProvenanceOutput

    • at_msrequired integer (uint64)

    • creatorrequired string

    • via optional string

  • questionrequired

    ExportQuestionOutput

    • idrequired string (uuid)

    • textrequired string

  • schemarequired string

  • sectionsrequired

    array of

    ExportSectionOutput

    • bodyrequired string

    • citationsrequired

      array of

      ExportCitationOutput

      • assertion_idrequired string (uuid)

      • citation_idrequired string (uuid)

      • rendered_footnoterequired string

    • kindrequired string

Example prompts

  • “Export the proof argument I just filed as a versioned document I can review or hand off.”
  • “Give me the full exported proof-argument document, with citations re-rendered live, not a frozen string.”

proof.argument.get

Read a single recorded proof argument by id, with its full section breakdown (supporting assertions, correlation reasoning, contrary-evidence treatments, certainty).

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • certaintyrequired string

  • claims_exhaustive_searchrequired boolean

  • conclusionrequired string

  • contrary_evidencerequired

    array of

    ContraryTreatmentOutput

    • assertionrequired string (uuid)

    • treatmentrequired string

  • correlation_reasoningrequired string

  • idrequired string (uuid)

  • questionrequired string (uuid)

  • supportingrequired array of string (uuid)

Example prompts

  • “Show me the full proof argument for this research question, including the certainty and contrary-evidence treatment.”
  • “Pull up the recorded proof argument by id and walk me through its reasoning.”

proof.argument.list

List recorded proof arguments, optionally filtered to one research question.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • question optional string (uuid)

  • tree_id string

Output shape

  • itemsrequired

    array of

    ProofArgumentView

    • certaintyrequired string

    • claims_exhaustive_searchrequired boolean

    • conclusionrequired string

    • contrary_evidencerequired

      array of

      ContraryTreatmentOutput

      • assertionrequired string (uuid)

      • treatmentrequired string

    • correlation_reasoningrequired string

    • idrequired string (uuid)

    • questionrequired string (uuid)

    • supportingrequired array of string (uuid)

  • next_cursor optional string

Example prompts

  • “List every proof argument filed for this research question.”
  • “Show me all recorded proof arguments across the tree.”

proof.argument.record

Record a GPS-standard proof argument: conclusion, certainty, supporting assertions, correlation reasoning, and contrary-evidence treatments. A refuted, pending-AI, or fabricated assertion cannot be cited — the domain's IneligibleAssertion error names which one, for both human and agent callers. Human actors apply directly; agent actors always file a proposal for human approval (D9) — a conclusion is human judgment.

Parameters

  • certaintyrequired string [enum: proved, probable, possible, disproved]

  • claims_exhaustive_searchrequired boolean

  • conclusionrequired string

  • contrary

    array of
    • assertionrequired string (uuid)

    • treatmentrequired string

  • correlation_reasoningrequired string

  • questionrequired string (uuid)

  • supportingrequired array of string (uuid)

  • tree_id string

Output shape

  • appliedrequired boolean

  • messagerequired string

  • proof_argument_id optional string (uuid)

  • proposal_id optional string (uuid)

Example prompts

  • “Draft and file a proof argument concluding Arthur was born in 1870, citing the confirmed census assertion and my correlation reasoning.”
  • “Record a proof argument for this question — remember, only confirmed assertions can be cited, not the one I just captured myself.”

FAN correlation

fan.network.query

Query the FAN (Friends, Associates, Neighbors) network for a persona or a concluded person: shared-event, typed-association, and place-co-occurrence edges within an optional date window. Read-only, and live rows only — refuted and superseded claims never appear as edges.

Parameters

  • cursor optional string

  • edge_types optional array of string [enum: shared_event, association, place_co_occurrence]

  • limit optional integer (uint32)

  • person_id optional string (uuid)

  • persona_id optional string (uuid)

  • tree_id string

  • window

    optional

Output shape

  • edgesrequired

    array of

    FanEdgeOutput

    • edge_typerequired string

    • evidencerequired array of string (uuid)

    • other_person optional string (uuid)

    • other_personarequired string (uuid)

    • viarequired

      FanEdgeViaOutput

      • association optional string (uuid)

      • event optional string (uuid)

      • own_event optional string (uuid)

      • own_role optional string

      • place_id optional string

      • relation optional string

      • their_event optional string (uuid)

      • their_role optional string

  • next_cursor optional string

Example prompts

  • “Who shares an association or event with Arthur Fernwood between 1870 and 1880?”
  • “Show me everyone connected to this persona through neighbors, witnesses, or shared events — I'm hunting for a FAN lead.”

Browse

assertion.list

List assertions — the evidence behind a person. Filter by persona (the usual entry point: person.card gives you a person, person.get its personas), by source, by kind, or by epistemic state; omit every filter for a tree-wide read. Rows are compact ids-and-claim (read one in full with assertion.get), so prefer source.cited_by when you want the claims on ONE source with their persona and person names resolved. Paginated (default 50 — echo next_cursor back as cursor to continue). This is how you find `confirmed` assertion ids, the only ones proof.argument.record can cite.

Parameters

  • cursor optional string

  • kind optional string [enum: fact, transcription_claim, event_participation, persona_association, name]

  • limit optional integer (uint32)

  • persona optional string (uuid)

  • source optional string (uuid)

  • state optional string [enum: unevaluated, cited_unverified, pending_ai_origin, confirmed, refuted, superseded]

  • tree_id string

Output shape

  • itemsrequired

    array of

    AssertionListItemView

    • claimrequired string

    • idrequired string (uuid)

    • kindrequired string

    • personarequired string (uuid)

    • sourcerequired string (uuid)

    • staterequired string

  • next_cursor optional string

Example prompts

  • “What's the evidence behind Temperance Wynwood's birth date?”
  • “List every claim we've captured from that 1880 census source.”
  • “Which of Arthur Fernwood's claims are confirmed and ready to cite?”

assertion.unsourced.list

List assertions still awaiting a human evaluation decision — unevaluated human captures and cited-but-unverified import claims. Agent-origin captures never appear here (they are pending_ai_origin, the review lane's own status); a source already attached doesn't remove a row either, since attaching evidence is not the same as confirming it. Each row carries the persona label and, if the persona has been concluded, the person it belongs to.

Parameters

  • tree_id string

Output shape

  • itemsrequired

    array of

    UnsourcedAssertionView

    • claimrequired string

    • idrequired string (uuid)

    • kindrequired string

    • person optional string (uuid)

    • personarequired string (uuid)

    • persona_labelrequired string

    • sourcerequired string (uuid)

    • staterequired string

Example prompts

  • “Which claims still need someone to review and confirm them?”
  • “Show me everything captured by hand that hasn't been evaluated yet.”
  • “List the assertions that came in with a citation but were never verified.”

conflict.list

List detected conflicts — sets of contradicting assertions about the same persona or identity. `open_only` narrows to the ones still unresolved, which is exactly what the `conflict_resolution` item on a research question's GPS checklist is asking about. Conflicts are resolved by writing a proof argument (proof.argument.record) and naming it to conflict.resolve, never by auto-merging. The argument alone closes nothing; until conflict.resolve runs, the GPS item stays unmet.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • open_only optional boolean

  • tree_id string

Output shape

  • detectionrequired

    ConflictDetectionBriefView

    • conflicts_recordedrequired integer (uint)

    • detection_has_runrequired boolean

    • imports_observedrequired integer (uint)

    • open_conflictsrequired integer (uint)

    • triggerrequired string

  • itemsrequired

    array of

    ConflictListItemView

    • assertionsrequired array of string (uuid)

    • idrequired string (uuid)

    • openrequired boolean

  • next_cursor optional string

Example prompts

  • “Are there any unresolved conflicts in this tree?”
  • “Show me the contradicting claims I still need to work through.”

person.card

One call orients you on one person: display name, lifespan years, and immediate family — parents, spouses, children, each with id + name + lifespan (derived server-side from the same marriage-event participant-role structure fan.network.query exposes) — plus live assertion and distinct-source counts as a cheap source-coverage summary. Pass expand (any of "events", "assertions", "sources") to inline those records instead of paying one call per record to fetch them; each expanded list caps at 100 rows. Follow up with person.get / fan.network.query for anything expand does not cover.

Parameters

  • expand array of string

  • personrequired string (uuid)

  • tree_id string

Output shape

  • assertion_countrequired integer (uint32)

  • assertions

    optional array of

    PersonCardAssertionView

    • claimrequired string

    • idrequired string (uuid)

    • kindrequired string

    • personarequired string (uuid)

    • persona_labelrequired string

    • sourcerequired string (uuid)

    • source_titlerequired string

    • staterequired string

  • birth_year optional integer (int32)

  • childrenrequired array of PersonSummaryView

  • death_year optional integer (int32)

  • events

    optional array of

    EventViewOutput

    • date

      optional

      GenealogicalDateInput (tagged union)

      kind = about

      kind = after

      kind = before

      kind = between

      • kindrequired string [const: between]

      • valuerequired array of any

      kind = exact

      kind = range

      • kindrequired string [const: range]

      • valuerequired array of any

    • date_phrase optional string

    • idrequired string (uuid)

    • kindrequired

      LifeEventKindInput

      • kindrequired any [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]

      • phrase string

    • kind_phrase optional string

    • participationsrequired

      array of

      EventParticipantViewOutput

      • assertionrequired string (uuid)

      • personarequired string (uuid)

      • persona_labelrequired string

      • rolerequired

        ParticipantRoleInput

        • phrase string

        • rolerequired any [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]

      • statusrequired string

    • place

      optional

      PlaceInput

      • authority_id optional string

      • coordinates

        optional

        CoordinatesInput

        • latitude_nanodegrequired integer (int64)

        • longitude_nanodegrequired integer (int64)

      • jurisdictionsrequired array of string

      • valid_time_note optional string

    • sourcerequired string (uuid)

    • source_titlerequired string

  • idrequired string (uuid)

  • namerequired string

  • parentsrequired array of PersonSummaryView

  • source_countrequired integer (uint32)

  • sources

    optional array of

    PersonCardSourceView

    • has_citationrequired boolean

    • idrequired string (uuid)

    • titlerequired string

  • spousesrequired array of PersonSummaryView

Example prompts

  • “Give me a quick overview of Clara Fernwood — parents, spouse, children, and how well sourced she is.”
  • “Who were Arthur Fernwood's children, and when did he live?”

person.duplicate_candidates

Check whether a person you are about to create likely already exists: ranks every person in the tree against the given/surname (and optional GEDCOM-syntax birth/death dates) you supply, using the same similarity scorer the app's own entry forms consult. At least one of given/surname is required. Returns only likely/possible matches, best first, each with id, name, lifespan years, a 0-1000 permille score, and its band. Call this before person.create; an empty list means nothing plausible exists. Advisory only — it never blocks creation.

Parameters

  • birth optional string

  • death optional string

  • given optional string

  • limit optional integer (uint32)

  • surname optional string

  • tree_id string

Output shape

  • candidatesrequired

    array of

    DuplicateCandidateView

    • birth_year optional integer (int32)

    • death_year optional integer (int32)

    • idrequired string (uuid)

    • likelihoodrequired string

    • namerequired string

    • score_permillerequired integer (uint16)

Example prompts

  • “Before you add John Smith born 1850 from this census, check whether he already exists in my tree.”
  • “I am about to enter Mary Jones, died 1901 — is there already someone like her here?”

person.get

Read a single concluded person (identity) by id.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • certaintyrequired string

  • idrequired string (uuid)

  • personasrequired array of string (uuid)

Example prompts

  • “Show me the concluded person record this persona has been merged into.”
  • “Look up this identity by id and tell me what's recorded about them.”

person.search

Search persons by name — substring, case-insensitive (the same prefix+fuzzy query plan as the app's own search box). Returns id, display name, and lifespan years per hit, best match first; paginated (default 50 — echo next_cursor back as cursor to continue). Start here to resolve a name to a person id.

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • queryrequired string

  • tree_id string

Output shape

  • hitsrequired

    array of

    PersonSummaryView

    • birth_year optional integer (int32)

    • death_year optional integer (int32)

    • idrequired string (uuid)

    • namerequired string

  • next_cursor optional string

Example prompts

  • “Find everyone named Fernwood in my tree.”
  • “Is there a Clara in this family file? Give me her id so we can dig in.”

persona.get

Read a single persona (a source appearance) by id.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • idrequired string (uuid)

  • labelrequired string

  • sourcerequired string (uuid)

Example prompts

  • “Show me everything recorded about this persona from that one source appearance.”
  • “Pull up the persona record for Arthur Fernwood as he appears in the 1900 census.”

project.state.get

The session brief: current investigation state in one bounded call — open questions with coverage, settled conclusions, retired leads with reasons, pending-lane depth, and conflict-detection state. Pass the previous brief's cursor as `since` to get only what changed. A digest with pointers; drill down with the research verbs.

Parameters

  • since optional string

  • tree_id string

Output shape

  • conflictsrequired

    ConflictDetectionBriefView

    • conflicts_recordedrequired integer (uint)

    • detection_has_runrequired boolean

    • imports_observedrequired integer (uint)

    • open_conflictsrequired integer (uint)

    • triggerrequired string

  • cursorrequired string

  • open_questionsrequired

    OpenQuestionsSectionView

    • itemsrequired

      array of

      OpenQuestionBriefView

      • gpsrequired

        ChecklistViewOutput

        • analysis_correlationrequired boolean

        • complete_citationsrequired boolean

        • conflict_resolutionrequired boolean

        • exhaustive_searchrequired

          ExhaustiveSearchStatusOutput (tagged union)

          status = claimable

          • statusrequired string [const: claimable]

          status = in_progress

          • coveredrequired integer (uint32)

          • statusrequired string [const: in_progress]

          • totalrequired integer (uint32)

          status = not_started

          • statusrequired string [const: not_started]

        • written_conclusionrequired boolean

      • idrequired string (uuid)

      • log_entriesrequired integer (uint)

      • plan_itemsrequired integer (uint)

      • questionrequired string

      • subjectsrequired integer (uint)

    • totalrequired integer (uint)

  • pendingrequired

    PendingLaneBriefView

    • deferredrequired integer (uint)

    • depthrequired integer (uint)

  • retiredrequired

    RetiredSectionView

    • itemsrequired

      array of

      RetiredLeadBriefView

      • closed_at_msrequired integer (uint64)

      • disposition optional string

      • idrequired string (uuid)

      • questionrequired string

      • reason optional string

    • totalrequired integer (uint)

  • rulesrequired array of string

  • settledrequired

    SettledSectionView

    • confirmed_assertionsrequired

      ConfirmedAssertionsSectionView

      • itemsrequired

        array of

        ConfirmedAssertionBriefView

        • claimrequired string

        • idrequired string (uuid)

      • totalrequired integer (uint)

    • proof_argumentsrequired

      ProofArgumentsSectionView

      • itemsrequired

        array of

        ProofArgumentBriefView

        • certaintyrequired string

        • conclusionrequired string

        • idrequired string (uuid)

        • questionrequired string (uuid)

        • recorded_at_msrequired integer (uint64)

      • totalrequired integer (uint)

  • since optional string

Example prompts

  • “Catch me up on this investigation — what's open, what's settled, and what have we ruled out?”
  • “What changed in the project since my last session?”
  • “Before we start anything new: which leads are retired, and why?”

settled.list

Settled knowledge and retired leads in ONE call — do not re-derive or re-open any of it. Returns: recorded conclusions (proof arguments), refuted/superseded assertions, negative findings (searched and found absent), contrary evidence a conclusion already considered and rejected, and retired leads (closed questions) with their dispositions and human-authored reasons. If a proposed conclusion touches retired ground the engine will refuse it and name the retirement; a retired lead is reopened only by the human, in the app.

Parameters

  • tree_id string

Output shape

  • conclusionsrequired

    array of

    SettledConclusionView

    • certaintyrequired string

    • conclusionrequired string

    • idrequired string (uuid)

    • questionrequired string (uuid)

    • question_textrequired string

    • recorded_at_msrequired integer (uint64)

    • recorded_byrequired string

  • negative_findingsrequired

    array of

    NegativeFindingView

    • expectationrequired string

    • inferencerequired string

    • log_entryrequired string (uuid)

    • question optional string (uuid)

    • recorded_at_msrequired integer (uint64)

    • recorded_byrequired string

    • search_context optional string

  • refutationsrequired

    array of

    RuledOutAssertionView

    • claimrequired string

    • idrequired string (uuid)

    • ruled_out_at_ms optional integer (uint64)

    • ruled_out_by optional string

    • statusrequired string

  • rejected_evidencerequired

    array of

    RejectedEvidenceView

    • assertionrequired string (uuid)

    • proof_argumentrequired string (uuid)

    • recorded_at_msrequired integer (uint64)

    • recorded_byrequired string

    • treatmentrequired string

  • retired_leadsrequired

    array of

    RetiredLeadView

    • closed_at_msrequired integer (uint64)

    • disposition optional string

    • idrequired string (uuid)

    • questionrequired string

    • reason optional string

Example prompts

  • “What has this investigation already settled or ruled out?”
  • “Show me every retired lead and the reason we stopped chasing it.”
  • “Which claims were refuted, and what did we search for and never find?”

source.get

Read a single source by id.

Parameters

  • idrequired string (uuid)

  • tree_id string

Output shape

  • blobsrequired array of string

  • derived_from optional string (uuid)

  • idrequired string (uuid)

  • titlerequired string

Example prompts

  • “Show me the full record for this source, including its citation.”
  • “Pull up the details on the census source I cited earlier.”

source.list

List the sources in this tree, each with the number of claims captured against it and whether it carries a formal citation. Use this to discover and cite existing evidence instead of creating a duplicate source; source.get reads one in full, and source.cited_by shows which claims rest on it. Paginated (default 50 — echo next_cursor back as cursor to continue).

Parameters

  • cursor optional string

  • limit optional integer (uint32)

  • tree_id string

Output shape

  • itemsrequired

    array of

    SourceListItemView

    • claim_countrequired integer (uint32)

    • has_citationrequired boolean

    • idrequired string (uuid)

    • titlerequired string

  • next_cursor optional string

Example prompts

  • “What sources do I already have in this family file?”
  • “Which of my sources still don't have a proper citation?”

tree.stats

Whole-tree orientation numbers: person count, birth-year span, top-N surname frequencies (default 10, max 50), and counts of persons missing birth/death dates. Read this first when meeting an unfamiliar tree.

Parameters

  • top_surnames optional integer (uint32)

  • tree_id string

Output shape

  • birth_year_max optional integer (int32)

  • birth_year_min optional integer (int32)

  • missing_birth_date_countrequired integer (uint32)

  • missing_death_date_countrequired integer (uint32)

  • person_countrequired integer (uint32)

  • top_surnamesrequired

    array of

    SurnameCountView

    • countrequired integer (uint32)

    • surnamerequired string

Example prompts

  • “How big is this tree, and which surnames dominate it?”
  • “How many people here are missing a birth or death date?”

Export & redaction

redaction.propose

Propose redacting (tombstoning) an event. This never executes directly — it only files a Proposal for a human to review and approve or deny. There is no tool that redacts directly; this is the only redaction-adjacent tool available to an agent.

Parameters

  • reasonrequired string

  • target_eventrequired string (uuid)

  • tree_id string

Output shape

  • messagerequired string

  • proposal_idrequired string (uuid)

Example prompts

  • “This event turned out to be about the wrong person — propose redacting it.”
  • “File a proposal to redact this life event so I can review it before it's tombstoned.”
Shared schemas15

GenealogicalDateInput

kind = about

kind = after

kind = before

kind = between

  • kindrequired string [const: between]

  • valuerequired array of any

kind = exact

kind = range

  • kindrequired string [const: range]

  • valuerequired array of any

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PartialDateInput

  • calendarrequired string [enum: gregorian, julian]

  • day optional integer (uint8)

  • dual_year optional integer (int32)

  • month optional integer (uint8)

  • yearrequired integer (int32)

PersonSummaryView

  • birth_year optional integer (int32)

  • death_year optional integer (int32)

  • idrequired string (uuid)

  • namerequired string

RenderedTextOutput

  • htmlrequired string

  • textrequired string

RenderedTextOutput

  • htmlrequired string

  • textrequired string

RenderedTextOutput

  • htmlrequired string

  • textrequired string

Prompts

Named, arguments-driven starting points a client can surface directly (e.g. as a slash command) rather than the model composing the request itself.

coverage-gap-review

Surface GPS coverage omissions: plan items without logs, questions without plans, and found-nothing searches never formalized into NegativeEvidence. Omit the question argument for a tree-wide review.

  • question

    Research question id (omit for a tree-wide review)

draft-proof-argument

Given a research question, review its GPS-checklist state and the eligible (proof-feedable) supporting assertions with open conflicts, then draft a structured proof argument section by section and file it via proof.argument.record.

  • questionrequired

    Research question id

plan-research-question

Given a research question, review its current plan/coverage state and the subject's FAN neighborhood, then propose candidate plan-item sources (and a FAN pivot when direct evidence is thin) via research.plan.item.add.

  • questionrequired

    Research question id

resume-investigation

Orient a cold session: read the live session brief (open questions, settled conclusions, retired leads with reasons, pending depth, conflict-detection state), state the store's refusal rules, and open with tree.stats and person.search. Pass a previous brief's cursor as `since` to see only what changed.

  • since

    A previous brief's cursor — restricts the brief to what changed while you were away (omit for the full digest)

Resources

Addressable, read-only heartwood:// URIs a client can fetch directly instead of calling a tool.

heartwood://methodology/epistemic-status

What each epistemic-status code means and what it implies for your next step. Read this once; records carry the bare code, never the explanation. Append ?tree_id=<id> to bind this read to a tree; absent reads the active tree (tree.list names it) and naming one never changes it.

MIME type: application/json

heartwood://project/state

The session brief — current investigation state as a bounded digest; append ?since=<cursor> for only what changed. Wraps project.state.get. Append ?tree_id=<id> to bind this read to a tree; absent reads the active tree (tree.list names it) and naming one never changes it.

MIME type: application/json

heartwood://research/coverage

Tree-wide GPS coverage summary — per-question checklist rollup. Append ?tree_id=<id> to bind this read to a tree; absent reads the active tree (tree.list names it) and naming one never changes it.

MIME type: application/json

heartwood://research/question/{id}

A research question, its subjects, and status. Append ?tree_id=<id> to bind this read to a tree; absent reads the active tree (tree.list names it) and naming one never changes it.

MIME type: application/json

heartwood://research/question/{id}/coverage

A research question's D2 GPS checklist. Append ?tree_id=<id> to bind this read to a tree; absent reads the active tree (tree.list names it) and naming one never changes it.

MIME type: application/json

heartwood://research/question/{id}/log

A research question's full log, attestation-labeled. Append ?tree_id=<id> to bind this read to a tree; absent reads the active tree (tree.list names it) and naming one never changes it.

MIME type: application/json

heartwood://research/question/{id}/plan

A research question's ordered plan items with done/coverage state. Append ?tree_id=<id> to bind this read to a tree; absent reads the active tree (tree.list names it) and naming one never changes it.

MIME type: application/json

heartwood://research/questions

Open research questions, compact list. Append ?tree_id=<id> to bind this read to a tree; absent reads the active tree (tree.list names it) and naming one never changes it.

MIME type: application/json