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"]
}
}
}
| Client | Where this config goes |
|---|---|
| Claude Desktop | claude_desktop_config.json
(Settings → Developer → Edit Config) — or use the
in-app “Connect” button, which writes this for
you. |
| Claude Code | project-local .mcp.json, or
run claude mcp add heartwood -- heartwood mcp |
| Cursor | project-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.
tree.listParameters
include_archivedboolean
Output shape
active_tree_idrequiredstringitemsrequiredarray ofTreeView
archivedrequiredbooleancreated_at_msrequiredinteger (uint64)idrequiredstringnamerequiredstring
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.
person.createParameters
certaintyrequiredstring [enum: proved, probable, possible, disproved]personasrequired array ofstring (uuid)proof_argumentoptionalstring (uuid)tree_idstring
Output shape
appliedrequiredbooleanmessagerequiredstringperson_idoptionalstring (uuid)proposal_idoptionalstring (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.
person.living_status.setParameters
personrequiredstring (uuid)statusrequiredstring [enum: living, deceased, use_heuristic]tree_idstring
Output shape
okrequiredboolean
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.
person.mergeParameters
leftrequiredstring (uuid)rightrequiredstring (uuid)tree_idstring
Output shape
person_idrequiredstring (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.
person.retractParameters
personrequiredstring (uuid)tree_idstring
Output shape
okrequiredboolean
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.
person.splitParameters
leftrequired array ofstring (uuid)personrequiredstring (uuid)rightrequired array ofstring (uuid)tree_idstring
Output shape
leftrequiredstring (uuid)rightrequiredstring (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.
persona.createParameters
labelrequiredstringnameoptionaldisplayoptionalstringgivenarray ofstringsurnamearray ofstring
research_contextoptionalsearch_contextrequiredstringsessionrequiredstring (uuid)
sourcerequiredstring (uuid)tree_idstring
Output shape
persona_idrequiredstring (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.
relationship.attachParameters
kindrequiredstringpersonrequiredstring (uuid)relativerequiredstring (uuid)research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
sourcerequiredstring (uuid)tree_idstring
Output shape
assertionsrequired array ofstring (uuid)createdrequiredbooleaneventrequiredstring (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.
relationship.reparentParameters
childrequiredstring (uuid)from_parentrequiredstring (uuid)research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
sourcerequiredstring (uuid)to_parentrequiredstring (uuid)tree_idstring
Output shape
assertionsrequired array ofstring (uuid)createdrequiredbooleaneventrequiredstring (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.”
relationship.unlink
Remove a parent or spouse edge between two persons — refutes or supersedes the underlying participation assertions, never deletes the persons or any other edge. Same `kind` vocabulary as relationship.attach. Requires research_context, same as assertion.capture.
relationship.unlinkParameters
kindrequiredstringpersonrequiredstring (uuid)relativerequiredstring (uuid)research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
tree_idstring
Output shape
okrequiredboolean
Example prompts
- “Remove the parent link I added by mistake — that census entry was for a different family.”
- “Undo the spouse relationship between these two; the marriage record turned out to be for someone else.”
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.
edit.undoParameters
eventoptionalstring (uuid)tree_idstring
Output shape
compensatingrequired array ofstring (uuid)undonerequiredstring (uuid)undone_event_typerequiredstring
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.
edit.undo_previewParameters
tree_idstring
Output shape
last_editoptionalEditDispositionView
eventrequiredstring (uuid)event_typerequiredstringundoablerequiredUndoableNowView
issueoptionalstringrationaleoptionalstringstaterequiredstring
targetoptionalUndoTargetView
at_msrequiredinteger (uint64)eventrequiredstring (uuid)event_typerequiredstring
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.
assignment.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
contentrequiredAssignmentContentView (tagged union)
kind=rawkindrequiredstring [const: raw]textrequiredstring
kind=structuredargumentsrequired map: string →stringkindrequiredstring [const: structured]prompt_namerequiredstring
idrequiredstring (uuid)recorded_at_msrequiredinteger (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.
session.currentParameters
tree_idstring
Output shape
actor_modeloptionalstringactor_reported_harnessoptionalstringcurrent_assignmentoptionalstring (uuid)event_countrequiredinteger (uint)first_seen_at_msrequiredinteger (uint64)last_seen_at_msrequiredinteger (uint64)sessionrequiredstring (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.
session.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
actor_modeloptionalstringactor_reported_harnessoptionalstringcurrent_assignmentoptionalstring (uuid)event_countrequiredinteger (uint)first_seen_at_msrequiredinteger (uint64)last_seen_at_msrequiredinteger (uint64)sessionrequiredstring (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.
assertion.captureParameters
attachedarray ofkind=external_recordidrequiredkind=ancestry_recorddbidrequiredstringkindrequiredstring [const: ancestry_record]recordrequiredstring
kind=doidoirequiredstringkindrequiredstring [const: doi]
kind=family_search_arkarkrequiredstringkindrequiredstring [const: family_search_ark]
kind=permalinkkindrequiredstring [const: permalink]urlrequiredstring
kindrequiredstring [const: external_record]
kind=offline_recordcitationrequiredstringkindrequiredstring [const: offline_record]
kind=source_blobblobrequiredstringkindrequiredstring [const: source_blob]
claimrequiredstringevidence_classoptionaldirectnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
kindrequiredkind=factkindrequiredstring [const: fact]
kind=transcription_claimkindrequiredstring [const: transcription_claim]of_blobrequiredstringregionoptionalheightrequirednumber (double)widthrequirednumber (double)xrequirednumber (double)yrequirednumber (double)
personarequiredstring (uuid)research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
sourcerequiredstring (uuid)tree_idstring
Output shape
claimrequiredstringepistemic_statusrequiredstringevent_contextoptionalEventContextOutput
dateoptionalGenealogicalDateInput (tagged union)
kind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
event_idrequiredstring (uuid)kindrequiredLifeEventKindInput
kindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringplaceoptionalPlaceInput
authority_idoptionalstringcoordinatesoptionalCoordinatesInput
latitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
sibling_participationsrequiredarray ofSiblingParticipationOutput
persona_idrequiredstring (uuid)persona_label_idrequiredstringrolerequiredParticipantRoleInput
phrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
statusrequiredstring
source_idrequiredstring (uuid)
evidence_classoptionalEvidenceClassInput
directnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
idrequiredstring (uuid)next_steprequiredstringpersonarequiredstring (uuid)sourcerequiredstring (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.
assertion.evidence.attachParameters
assertionrequiredstring (uuid)evidencerequiredkind=external_recordidrequiredkind=ancestry_recorddbidrequiredstringkindrequiredstring [const: ancestry_record]recordrequiredstring
kind=doidoirequiredstringkindrequiredstring [const: doi]
kind=family_search_arkarkrequiredstringkindrequiredstring [const: family_search_ark]
kind=permalinkkindrequiredstring [const: permalink]urlrequiredstring
kindrequiredstring [const: external_record]
kind=offline_recordcitationrequiredstringkindrequiredstring [const: offline_record]
kind=source_blobblobrequiredstringkindrequiredstring [const: source_blob]
tree_idstring
Output shape
claimrequiredstringepistemic_statusrequiredstringevent_contextoptionalEventContextOutput
dateoptionalGenealogicalDateInput (tagged union)
kind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
event_idrequiredstring (uuid)kindrequiredLifeEventKindInput
kindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringplaceoptionalPlaceInput
authority_idoptionalstringcoordinatesoptionalCoordinatesInput
latitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
sibling_participationsrequiredarray ofSiblingParticipationOutput
persona_idrequiredstring (uuid)persona_label_idrequiredstringrolerequiredParticipantRoleInput
phrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
statusrequiredstring
source_idrequiredstring (uuid)
evidence_classoptionalEvidenceClassInput
directnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
idrequiredstring (uuid)next_steprequiredstringpersonarequiredstring (uuid)sourcerequiredstring (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`.
assertion.supersedeParameters
assertionrequiredstring (uuid)byrequiredstring (uuid)tree_idstring
Output shape
conflicts_still_openrequired array ofstring (uuid)okrequiredboolean
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.
association.recordParameters
attachedarray ofkind=external_recordidrequiredkind=ancestry_recorddbidrequiredstringkindrequiredstring [const: ancestry_record]recordrequiredstring
kind=doidoirequiredstringkindrequiredstring [const: doi]
kind=family_search_arkarkrequiredstringkindrequiredstring [const: family_search_ark]
kind=permalinkkindrequiredstring [const: permalink]urlrequiredstring
kindrequiredstring [const: external_record]
kind=offline_recordcitationrequiredstringkindrequiredstring [const: offline_record]
kind=source_blobblobrequiredstringkindrequiredstring [const: source_blob]
evidence_classoptionaldirectnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
otherrequiredstring (uuid)personarequiredstring (uuid)relationrequiredrelation=employerrelationrequiredstring [const: employer]
relation=friend_or_associaterelationrequiredstring [const: friend_or_associate]
relation=godparentrelationrequiredstring [const: godparent]
relation=neighborrelationrequiredstring [const: neighbor]
relation=otherphraserequiredstringrelationrequiredstring [const: other]
research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
sourcerequiredstring (uuid)tree_idstring
Output shape
assertion_idrequiredstring (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.
event.correctParameters
dateoptionalkind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
eventrequiredstring (uuid)evidencearray ofkind=external_recordidrequiredkind=ancestry_recorddbidrequiredstringkindrequiredstring [const: ancestry_record]recordrequiredstring
kind=doidoirequiredstringkindrequiredstring [const: doi]
kind=family_search_arkarkrequiredstringkindrequiredstring [const: family_search_ark]
kind=permalinkkindrequiredstring [const: permalink]urlrequiredstring
kindrequiredstring [const: external_record]
kind=offline_recordcitationrequiredstringkindrequiredstring [const: offline_record]
kind=source_blobblobrequiredstringkindrequiredstring [const: source_blob]
fieldrequiredstringkindoptionalkindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringplaceoptionalauthority_idoptionalstringcoordinatesoptionallatitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
source_idrequiredstring (uuid)tree_idstring
Output shape
appliedrequiredbooleanmessagerequiredstringproposal_idoptionalstring (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.
event.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
dateoptionalGenealogicalDateInput (tagged union)
kind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
date_phraseoptionalstringidrequiredstring (uuid)kindrequiredLifeEventKindInput
kindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringparticipationsrequiredarray ofEventParticipantViewOutput
assertionrequiredstring (uuid)personarequiredstring (uuid)persona_labelrequiredstringrolerequiredParticipantRoleInput
phrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
statusrequiredstring
placeoptionalPlaceInput
authority_idoptionalstringcoordinatesoptionalCoordinatesInput
latitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
sourcerequiredstring (uuid)source_titlerequiredstring
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.
event.participant.addParameters
attachedarray ofkind=external_recordidrequiredkind=ancestry_recorddbidrequiredstringkindrequiredstring [const: ancestry_record]recordrequiredstring
kind=doidoirequiredstringkindrequiredstring [const: doi]
kind=family_search_arkarkrequiredstringkindrequiredstring [const: family_search_ark]
kind=permalinkkindrequiredstring [const: permalink]urlrequiredstring
kindrequiredstring [const: external_record]
kind=offline_recordcitationrequiredstringkindrequiredstring [const: offline_record]
kind=source_blobblobrequiredstringkindrequiredstring [const: source_blob]
eventrequiredstring (uuid)evidence_classoptionaldirectnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
participant_rolerequiredphrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
personarequiredstring (uuid)research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
sourcerequiredstring (uuid)tree_idstring
Output shape
createdrequiredbooleanparticipation_idrequiredstring (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.
event.recordParameters
dateoptionalkind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
kindrequiredkindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringparticipantsrequiredarray ofattachedarray ofkind=external_recordidrequiredkind=ancestry_recorddbidrequiredstringkindrequiredstring [const: ancestry_record]recordrequiredstring
kind=doidoirequiredstringkindrequiredstring [const: doi]
kind=family_search_arkarkrequiredstringkindrequiredstring [const: family_search_ark]
kind=permalinkkindrequiredstring [const: permalink]urlrequiredstring
kindrequiredstring [const: external_record]
kind=offline_recordcitationrequiredstringkindrequiredstring [const: offline_record]
kind=source_blobblobrequiredstringkindrequiredstring [const: source_blob]
evidence_classoptionaldirectnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
personarequiredstring (uuid)rolerequiredphrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
placeoptionalauthority_idoptionalstringcoordinatesoptionallatitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
sourcerequiredstring (uuid)tree_idstring
Output shape
event_idrequiredstring (uuid)participation_idsrequired array ofstring (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.
note.addParameters
anchorsarray ofkind=assertionidrequiredstring (uuid)kindrequiredstring [const: assertion]
kind=citationidrequiredstring (uuid)kindrequiredstring [const: citation]
kind=life_eventidrequiredstring (uuid)kindrequiredstring [const: life_event]
kind=personidrequiredstring (uuid)kindrequiredstring [const: person]
kind=personaidrequiredstring (uuid)kindrequiredstring [const: persona]
kind=sourceidrequiredstring (uuid)kindrequiredstring [const: source]
citationsarray ofstring (uuid)textrequiredstringtree_idstring
Output shape
note_idrequiredstring (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.
persona.name_pieces.setParameters
personarequiredstring (uuid)piecesrequireddisplayrequiredstringgivenarray ofstringname_typeoptionaltype=akatyperequiredstring [const: aka]
type=birthtyperequiredstring [const: birth]
type=immigranttyperequiredstring [const: immigrant]
type=maidentyperequiredstring [const: maiden]
type=marriedtyperequiredstring [const: married]
type=otherphraserequiredstringtyperequiredstring [const: other]
type=professionaltyperequiredstring [const: professional]
nicknamearray ofstringprefixarray ofstringsuffixarray ofstringsurnamearray ofstringsurname_prefixarray ofstring
research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
tree_idstring
Output shape
setrequiredboolean
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.
transcription.claimParameters
claimrequiredstringof_blobrequiredstringpersonarequiredstring (uuid)regionoptionalheightrequirednumber (double)widthrequirednumber (double)xrequirednumber (double)yrequirednumber (double)
research_contextrequiredsearch_contextrequiredstringsessionrequiredstring (uuid)
sourcerequiredstring (uuid)tree_idstring
Output shape
claimrequiredstringepistemic_statusrequiredstringevent_contextoptionalEventContextOutput
dateoptionalGenealogicalDateInput (tagged union)
kind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
event_idrequiredstring (uuid)kindrequiredLifeEventKindInput
kindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringplaceoptionalPlaceInput
authority_idoptionalstringcoordinatesoptionalCoordinatesInput
latitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
sibling_participationsrequiredarray ofSiblingParticipationOutput
persona_idrequiredstring (uuid)persona_label_idrequiredstringrolerequiredParticipantRoleInput
phrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
statusrequiredstring
source_idrequiredstring (uuid)
evidence_classoptionalEvidenceClassInput
directnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
idrequiredstring (uuid)next_steprequiredstringpersonarequiredstring (uuid)sourcerequiredstring (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.
citation.createParameters
elementsrequiredarray ofkind=access_datekindrequiredstring [const: access_date]valuerequiredstring
kind=authorkindrequiredstring [const: author]valuerequiredstring
kind=free_textkindrequiredstring [const: free_text]valuerequiredstring
kind=identifieridrequiredkind=ancestry_recorddbidrequiredstringkindrequiredstring [const: ancestry_record]recordrequiredstring
kind=doidoirequiredstringkindrequiredstring [const: doi]
kind=family_search_arkarkrequiredstringkindrequiredstring [const: family_search_ark]
kind=permalinkkindrequiredstring [const: permalink]urlrequiredstring
kindrequiredstring [const: identifier]
kind=locatorkindrequiredstring [const: locator]valuerequiredstring
kind=otherkindrequiredstring [const: other]labelrequiredstringvaluerequiredstring
kind=publicationkindrequiredstring [const: publication]valuerequiredstring
kind=repositorykindrequiredstring [const: repository]valuerequiredstring
kind=source_typekindrequiredstring [const: source_type]valuerequiredstring
kind=titlekindrequiredstring [const: title]valuerequiredstring
tree_idstring
Output shape
citation_idrequiredstring (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.
citation.elements.setParameters
citation_idrequiredstring (uuid)elementsrequired map: string →stringtree_idstring
Output shape
appliedrequiredbooleanmessagerequiredstringproposal_idoptionalstring (uuid)renderedoptionalCitationRenderOutput
bibliographyoptionalRenderedTextOutputcslrequired optionalobjectfirstoptionalRenderedTextOutputsubsequentoptionalRenderedTextOutputwarningsrequired array ofstring
revisionoptionalinteger (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.
citation.renderParameters
citation_idoptionalstring (uuid)elementsmap: string →stringformstring [enum: first, subsequent, bibliography, all]template_idoptionalstringtree_idstring
Output shape
bibliographyoptionalRenderedTextOutputcslrequired optionalobjectfirstoptionalRenderedTextOutputsubsequentoptionalRenderedTextOutputwarningsrequired array ofstring
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.
citation.source_type.setParameters
citation_idrequiredstring (uuid)template_idrequiredstringtree_idstring
Output shape
appliedrequiredbooleanmessagerequiredstringmissing_requiredoptional array ofstringproposal_idoptionalstring (uuid)revisionoptionalinteger (uint32)template_idoptionalstring
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.
citation.templates.listParameters
categoryoptionalstringtree_idstring
Output shape
registry_versionrequiredinteger (uint32)templatesrequiredarray ofCitationTemplateSummary
categoryrequiredstringfieldsrequiredarray ofCitationTemplateFieldSummary
elementrequiredstringidrequiredstringlabelrequiredstringrequiredrequiredboolean
idrequiredstringlabelrequiredstring
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.
source.attach_blobParameters
blobrequiredstringsourcerequiredstring (uuid)tree_idstring
Output shape
blobsrequired array ofstringderived_fromoptionalstring (uuid)idrequiredstring (uuid)titlerequiredstring
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.
source.cited_byParameters
cursoroptionalstringlimitoptionalinteger (uint32)source_idrequiredstring (uuid)tree_idstring
Output shape
claimsrequiredarray ofSourceCitedClaimView
assertionrequiredstring (uuid)claimrequiredstringepistemic_statusrequiredstringkindrequiredstringpersonoptionalstring (uuid)person_nameoptionalstringpersonarequiredstring (uuid)persona_labelrequiredstring
next_cursoroptionalstringtotalrequiredinteger (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.
source.createParameters
elementsrequired map: string →stringtemplate_idrequiredstringtitleoptionalstringtree_idstring
Output shape
citation_idrequiredstring (uuid)createdrequiredbooleanrenderedrequiredCitationRenderOutput
bibliographyoptionalRenderedTextOutputcslrequired optionalobjectfirstoptionalRenderedTextOutputsubsequentoptionalRenderedTextOutputwarningsrequired array ofstring
source_idrequiredstring (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.
research.log.captureParameters
found_nothingbooleanpersonaoptionalstring (uuid)questionoptionalstring (uuid)search_contextrequiredstringsessionoptionalstring (uuid)sourceoptionalstring (uuid)tree_idstring
Output shape
linkedrequiredResearchLogCaptureLinkedOutput
personaoptionalstring (uuid)plan_itemoptionalstring (uuid)sourceoptionalstring (uuid)
log_entry_idrequiredstring (uuid)session_idrequiredstring (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).
research.log.detail.appendParameters
detailrequiredstringlog_entryrequiredstring (uuid)tree_idstring
Output shape
log_entry_idrequiredstring (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.
research.log.entry.addParameters
search_contextrequiredstringsessionrequiredstring (uuid)tree_idstring
Output shape
log_entry_idrequiredstring (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.
research.log.entry.retractParameters
log_entryrequiredstring (uuid)tree_idstring
Output shape
okrequiredboolean
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.
research.log.entry.text.setParameters
log_entryrequiredstring (uuid)search_contextrequiredstringtree_idstring
Output shape
okrequiredboolean
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.
research.log.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
attestationrequiredstringdetailsrequiredarray ofLogDetailView
attestationrequiredstringdetailrequiredstringidrequiredstring (uuid)recorded_atrequiredinteger (int64)
found_nothingrequiredbooleanidrequiredstring (uuid)personaoptionalstring (uuid)plan_itemoptionalstring (uuid)questionoptionalstring (uuid)recorded_atrequiredinteger (int64)search_contextrequiredstringsessionrequiredstring (uuid)sourceoptionalstring (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.
research.log.listParameters
cursoroptionalstringlimitoptionalinteger (uint32)personaoptionalstring (uuid)questionoptionalstring (uuid)sessionoptionalstring (uuid)sourceoptionalstring (uuid)tree_idstring
Output shape
entriesrequiredarray ofResearchLogListEntryView
attestationrequiredstringdetailsrequiredarray ofLogDetailView
attestationrequiredstringdetailrequiredstringidrequiredstring (uuid)recorded_atrequiredinteger (int64)
found_nothingrequiredbooleanidrequiredstring (uuid)personaoptionalstring (uuid)plan_itemoptionalstring (uuid)questionoptionalstring (uuid)recorded_atrequiredinteger (int64)search_contextrequiredstringsessionrequiredstring (uuid)sourceoptionalstring (uuid)
next_cursoroptionalstringtotalrequiredinteger (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.
research.log.negative.recordParameters
expectationrequiredstringinferencerequiredstringlog_entryrequiredstring (uuid)personarequiredstring (uuid)tree_idstring
Output shape
negative_evidence_idrequiredstring (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.
research.question.closeParameters
dispositionoptionalstring [enum: answered, abandoned, superseded, merged]idrequiredstring (uuid)reasonoptionalstringtree_idstring
Output shape
closedrequiredboolean
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.
research.question.log.getParameters
cursoroptionalstringlimitoptionalinteger (uint32)questionrequiredstring (uuid)tree_idstring
Output shape
entriesrequiredarray ofLogEntryView
attestationrequiredstringidrequiredstring (uuid)search_contextrequiredstringsessionrequiredstring (uuid)
next_cursoroptionalstring
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.
research.question.openParameters
questionrequiredstringtree_idstring
Output shape
research_question_idrequiredstring (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.
research.session.endParameters
sessionrequiredstring (uuid)tree_idstring
Output shape
endedrequiredboolean
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.
research.session.startParameters
questionrequiredstring (uuid)tree_idstring
Output shape
session_idrequiredstring (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.
research.plan.getParameters
questionrequiredstring (uuid)tree_idstring
Output shape
coveragerequiredChecklistViewOutput
analysis_correlationrequiredbooleancomplete_citationsrequiredbooleanconflict_resolutionrequiredbooleanexhaustive_searchrequiredExhaustiveSearchStatusOutput (tagged union)
status=claimablestatusrequiredstring [const: claimable]
status=in_progresscoveredrequiredinteger (uint32)statusrequiredstring [const: in_progress]totalrequiredinteger (uint32)
status=not_startedstatusrequiredstring [const: not_started]
written_conclusionrequiredboolean
itemsrequiredarray ofPlanItemView
descriptionrequiredstringdonerequiredbooleanidrequiredstring (uuid)log_entry_countrequiredinteger (uint32)positionrequiredinteger (uint32)source_idoptionalstring (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.
research.plan.item.addParameters
descriptionrequiredstringpositionoptionalinteger (uint32)questionrequiredstring (uuid)source_idoptionalstring (uuid)tree_idstring
Output shape
item_idrequiredstring (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.
research.plan.item.description.setParameters
descriptionrequiredstringitemrequiredstring (uuid)tree_idstring
Output shape
okrequiredboolean
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).
research.plan.item.done.setParameters
donerequiredbooleanitemrequiredstring (uuid)tree_idstring
Output shape
donerequiredbooleanitemrequiredstring (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.
research.plan.item.removeParameters
itemrequiredstring (uuid)tree_idstring
Output shape
okrequiredboolean
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.
research.plan.item.reorderParameters
itemrequiredstring (uuid)positionrequiredinteger (uint32)tree_idstring
Output shape
okrequiredboolean
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).
research.question.coverage.getParameters
questionrequiredstring (uuid)tree_idstring
Output shape
gpsrequiredChecklistViewOutput
analysis_correlationrequiredbooleancomplete_citationsrequiredbooleanconflict_resolutionrequiredbooleanexhaustive_searchrequiredExhaustiveSearchStatusOutput (tagged union)
status=claimablestatusrequiredstring [const: claimable]
status=in_progresscoveredrequiredinteger (uint32)statusrequiredstring [const: in_progress]totalrequiredinteger (uint32)
status=not_startedstatusrequiredstring [const: not_started]
written_conclusionrequiredboolean
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.
research.question.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
idrequiredstring (uuid)questionrequiredstringstatusrequiredstringsubjectsrequiredarray ofSubjectRefInput (tagged union)
kind=life_eventidrequiredstring (uuid)kindrequiredstring [const: life_event]
kind=personidrequiredstring (uuid)kindrequiredstring [const: person]
kind=personaidrequiredstring (uuid)kindrequiredstring [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.
research.question.listParameters
cursoroptionalstringlimitoptionalinteger (uint32)statusoptionalstring [enum: open, closed]subjectoptionalkind=life_eventidrequiredstring (uuid)kindrequiredstring [const: life_event]
kind=personidrequiredstring (uuid)kindrequiredstring [const: person]
kind=personaidrequiredstring (uuid)kindrequiredstring [const: persona]
tree_idstring
Output shape
itemsrequiredarray ofQuestionView
idrequiredstring (uuid)questionrequiredstringstatusrequiredstringsubjectsrequiredarray ofSubjectRefInput (tagged union)
kind=life_eventidrequiredstring (uuid)kindrequiredstring [const: life_event]
kind=personidrequiredstring (uuid)kindrequiredstring [const: person]
kind=personaidrequiredstring (uuid)kindrequiredstring [const: persona]
next_cursoroptionalstring
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).
research.question.subjects.setParameters
questionrequiredstring (uuid)subjectsrequiredarray ofkind=life_eventidrequiredstring (uuid)kindrequiredstring [const: life_event]
kind=personidrequiredstring (uuid)kindrequiredstring [const: person]
kind=personaidrequiredstring (uuid)kindrequiredstring [const: persona]
tree_idstring
Output shape
questionrequiredQuestionView
idrequiredstring (uuid)questionrequiredstringstatusrequiredstringsubjectsrequiredarray ofSubjectRefInput (tagged union)
kind=life_eventidrequiredstring (uuid)kindrequiredstring [const: life_event]
kind=personidrequiredstring (uuid)kindrequiredstring [const: person]
kind=personaidrequiredstring (uuid)kindrequiredstring [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.
research.question.text.setParameters
questionrequiredstring (uuid)textrequiredstringtree_idstring
Output shape
okrequiredboolean
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.
assertion.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
claimrequiredstringepistemic_statusrequiredstringevent_contextoptionalEventContextOutput
dateoptionalGenealogicalDateInput (tagged union)
kind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
event_idrequiredstring (uuid)kindrequiredLifeEventKindInput
kindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringplaceoptionalPlaceInput
authority_idoptionalstringcoordinatesoptionalCoordinatesInput
latitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
sibling_participationsrequiredarray ofSiblingParticipationOutput
persona_idrequiredstring (uuid)persona_label_idrequiredstringrolerequiredParticipantRoleInput
phrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
statusrequiredstring
source_idrequiredstring (uuid)
evidence_classoptionalEvidenceClassInput
directnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
idrequiredstring (uuid)next_steprequiredstringpersonarequiredstring (uuid)sourcerequiredstring (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.
assertion.pending.listParameters
cursoroptionalstringlimitoptionalinteger (uint32)tree_idstring
Output shape
epistemic_statusrequiredstringitemsrequiredarray ofPendingAssertionView
claimrequiredstringevent_contextoptionalEventContextOutput
dateoptionalGenealogicalDateInput (tagged union)
kind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
event_idrequiredstring (uuid)kindrequiredLifeEventKindInput
kindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringplaceoptionalPlaceInput
authority_idoptionalstringcoordinatesoptionalCoordinatesInput
latitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
sibling_participationsrequiredarray ofSiblingParticipationOutput
persona_idrequiredstring (uuid)persona_label_idrequiredstringrolerequiredParticipantRoleInput
phrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
statusrequiredstring
source_idrequiredstring (uuid)
evidence_classoptionalEvidenceClassInput
directnessrequiredstring [enum: direct, indirect]informantrequiredstring [enum: primary, secondary]originalityrequiredstring [enum: original, derivative]
idrequiredstring (uuid)personarequiredstring (uuid)sourcerequiredstring (uuid)
next_cursoroptionalstringnext_steprequiredstringstaterequiredstring
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.
proposal.listParameters
cursoroptionalstringlimitoptionalinteger (uint32)tree_idstring
Output shape
itemsrequiredarray ofProposalView
idrequiredstring (uuid)op_kindrequiredstringstatusrequiredstring
next_cursoroptionalstringnext_steprequiredstring
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.
conflict.resolveParameters
conflictrequiredstring (uuid)proof_argumentrequiredstring (uuid)tree_idstring
Output shape
conflictrequiredstring (uuid)open_conflicts_remainingrequiredinteger (uint)proof_argumentrequiredstring (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.
proof.argument.exportParameters
idrequiredstring (uuid)tree_idstring
Output shape
certaintyrequiredstringclaims_exhaustive_searchrequiredbooleangps_checklistrequiredChecklistViewOutput
analysis_correlationrequiredbooleancomplete_citationsrequiredbooleanconflict_resolutionrequiredbooleanexhaustive_searchrequiredExhaustiveSearchStatusOutput (tagged union)
status=claimablestatusrequiredstring [const: claimable]
status=in_progresscoveredrequiredinteger (uint32)statusrequiredstring [const: in_progress]totalrequiredinteger (uint32)
status=not_startedstatusrequiredstring [const: not_started]
written_conclusionrequiredboolean
idrequiredstring (uuid)provenancerequiredExportProvenanceOutput
at_msrequiredinteger (uint64)creatorrequiredstringviaoptionalstring
questionrequiredExportQuestionOutput
idrequiredstring (uuid)textrequiredstring
schemarequiredstringsectionsrequiredarray ofExportSectionOutput
bodyrequiredstringcitationsrequiredarray ofExportCitationOutput
assertion_idrequiredstring (uuid)citation_idrequiredstring (uuid)rendered_footnoterequiredstring
kindrequiredstring
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).
proof.argument.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
certaintyrequiredstringclaims_exhaustive_searchrequiredbooleanconclusionrequiredstringcontrary_evidencerequiredarray ofContraryTreatmentOutput
assertionrequiredstring (uuid)treatmentrequiredstring
correlation_reasoningrequiredstringidrequiredstring (uuid)questionrequiredstring (uuid)supportingrequired array ofstring (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.
proof.argument.listParameters
cursoroptionalstringlimitoptionalinteger (uint32)questionoptionalstring (uuid)tree_idstring
Output shape
itemsrequiredarray ofProofArgumentView
certaintyrequiredstringclaims_exhaustive_searchrequiredbooleanconclusionrequiredstringcontrary_evidencerequiredarray ofContraryTreatmentOutput
assertionrequiredstring (uuid)treatmentrequiredstring
correlation_reasoningrequiredstringidrequiredstring (uuid)questionrequiredstring (uuid)supportingrequired array ofstring (uuid)
next_cursoroptionalstring
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.
proof.argument.recordParameters
certaintyrequiredstring [enum: proved, probable, possible, disproved]claims_exhaustive_searchrequiredbooleanconclusionrequiredstringcontraryarray ofassertionrequiredstring (uuid)treatmentrequiredstring
correlation_reasoningrequiredstringquestionrequiredstring (uuid)supportingrequired array ofstring (uuid)tree_idstring
Output shape
appliedrequiredbooleanmessagerequiredstringproof_argument_idoptionalstring (uuid)proposal_idoptionalstring (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.
fan.network.queryParameters
cursoroptionalstringedge_typesoptional array ofstring [enum: shared_event, association, place_co_occurrence]limitoptionalinteger (uint32)person_idoptionalstring (uuid)persona_idoptionalstring (uuid)tree_idstringwindowoptionalendrequiredGenealogicalDateInputstartrequiredGenealogicalDateInput
Output shape
edgesrequiredarray ofFanEdgeOutput
edge_typerequiredstringevidencerequired array ofstring (uuid)other_personoptionalstring (uuid)other_personarequiredstring (uuid)viarequiredFanEdgeViaOutput
associationoptionalstring (uuid)eventoptionalstring (uuid)own_eventoptionalstring (uuid)own_roleoptionalstringplace_idoptionalstringrelationoptionalstringtheir_eventoptionalstring (uuid)their_roleoptionalstring
next_cursoroptionalstring
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.
assertion.listParameters
cursoroptionalstringkindoptionalstring [enum: fact, transcription_claim, event_participation, persona_association, name]limitoptionalinteger (uint32)personaoptionalstring (uuid)sourceoptionalstring (uuid)stateoptionalstring [enum: unevaluated, cited_unverified, pending_ai_origin, confirmed, refuted, superseded]tree_idstring
Output shape
itemsrequiredarray ofAssertionListItemView
claimrequiredstringidrequiredstring (uuid)kindrequiredstringpersonarequiredstring (uuid)sourcerequiredstring (uuid)staterequiredstring
next_cursoroptionalstring
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.
assertion.unsourced.listParameters
tree_idstring
Output shape
itemsrequiredarray ofUnsourcedAssertionView
claimrequiredstringidrequiredstring (uuid)kindrequiredstringpersonoptionalstring (uuid)personarequiredstring (uuid)persona_labelrequiredstringsourcerequiredstring (uuid)staterequiredstring
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.
conflict.listParameters
cursoroptionalstringlimitoptionalinteger (uint32)open_onlyoptionalbooleantree_idstring
Output shape
detectionrequiredConflictDetectionBriefView
conflicts_recordedrequiredinteger (uint)detection_has_runrequiredbooleanimports_observedrequiredinteger (uint)open_conflictsrequiredinteger (uint)triggerrequiredstring
itemsrequiredarray ofConflictListItemView
assertionsrequired array ofstring (uuid)idrequiredstring (uuid)openrequiredboolean
next_cursoroptionalstring
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.
person.cardParameters
expandarray ofstringpersonrequiredstring (uuid)tree_idstring
Output shape
assertion_countrequiredinteger (uint32)assertionsoptional array ofPersonCardAssertionView
claimrequiredstringidrequiredstring (uuid)kindrequiredstringpersonarequiredstring (uuid)persona_labelrequiredstringsourcerequiredstring (uuid)source_titlerequiredstringstaterequiredstring
birth_yearoptionalinteger (int32)childrenrequired array ofPersonSummaryViewdeath_yearoptionalinteger (int32)eventsoptional array ofEventViewOutput
dateoptionalGenealogicalDateInput (tagged union)
kind=aboutkindrequiredstring [const: about]valuerequiredPartialDateInput
kind=afterkindrequiredstring [const: after]valuerequiredPartialDateInput
kind=beforekindrequiredstring [const: before]valuerequiredPartialDateInput
kind=betweenkindrequiredstring [const: between]valuerequired array ofany
kind=exactkindrequiredstring [const: exact]valuerequiredPartialDateInput
kind=rangekindrequiredstring [const: range]valuerequired array ofany
date_phraseoptionalstringidrequiredstring (uuid)kindrequiredLifeEventKindInput
kindrequiredany [enum: birth, death, marriage, divorce, marriage_banns, engagement, baptism, christening, burial, cremation, adoption, census, residence, emigration, immigration, naturalization, probate, will, graduation, retirement, other]phrasestring
kind_phraseoptionalstringparticipationsrequiredarray ofEventParticipantViewOutput
assertionrequiredstring (uuid)personarequiredstring (uuid)persona_labelrequiredstringrolerequiredParticipantRoleInput
phrasestringrolerequiredany [enum: principal, spouse, parent, child, witness, informant, officiant, clergy, godparent, enumerator, household_member, head_of_household, other]
statusrequiredstring
placeoptionalPlaceInput
authority_idoptionalstringcoordinatesoptionalCoordinatesInput
latitude_nanodegrequiredinteger (int64)longitude_nanodegrequiredinteger (int64)
jurisdictionsrequired array ofstringvalid_time_noteoptionalstring
sourcerequiredstring (uuid)source_titlerequiredstring
idrequiredstring (uuid)namerequiredstringparentsrequired array ofPersonSummaryViewsource_countrequiredinteger (uint32)sourcesoptional array ofPersonCardSourceView
has_citationrequiredbooleanidrequiredstring (uuid)titlerequiredstring
spousesrequired array ofPersonSummaryView
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.
person.duplicate_candidatesParameters
birthoptionalstringdeathoptionalstringgivenoptionalstringlimitoptionalinteger (uint32)surnameoptionalstringtree_idstring
Output shape
candidatesrequiredarray ofDuplicateCandidateView
birth_yearoptionalinteger (int32)death_yearoptionalinteger (int32)idrequiredstring (uuid)likelihoodrequiredstringnamerequiredstringscore_permillerequiredinteger (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.
person.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
certaintyrequiredstringidrequiredstring (uuid)personasrequired array ofstring (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.
person.searchParameters
cursoroptionalstringlimitoptionalinteger (uint32)queryrequiredstringtree_idstring
Output shape
hitsrequiredarray ofPersonSummaryView
birth_yearoptionalinteger (int32)death_yearoptionalinteger (int32)idrequiredstring (uuid)namerequiredstring
next_cursoroptionalstring
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.
persona.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
idrequiredstring (uuid)labelrequiredstringsourcerequiredstring (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.
project.state.getParameters
sinceoptionalstringtree_idstring
Output shape
conflictsrequiredConflictDetectionBriefView
conflicts_recordedrequiredinteger (uint)detection_has_runrequiredbooleanimports_observedrequiredinteger (uint)open_conflictsrequiredinteger (uint)triggerrequiredstring
cursorrequiredstringopen_questionsrequiredOpenQuestionsSectionView
itemsrequiredarray ofOpenQuestionBriefView
gpsrequiredChecklistViewOutput
analysis_correlationrequiredbooleancomplete_citationsrequiredbooleanconflict_resolutionrequiredbooleanexhaustive_searchrequiredExhaustiveSearchStatusOutput (tagged union)
status=claimablestatusrequiredstring [const: claimable]
status=in_progresscoveredrequiredinteger (uint32)statusrequiredstring [const: in_progress]totalrequiredinteger (uint32)
status=not_startedstatusrequiredstring [const: not_started]
written_conclusionrequiredboolean
idrequiredstring (uuid)log_entriesrequiredinteger (uint)plan_itemsrequiredinteger (uint)questionrequiredstringsubjectsrequiredinteger (uint)
totalrequiredinteger (uint)
pendingrequiredPendingLaneBriefView
deferredrequiredinteger (uint)depthrequiredinteger (uint)
retiredrequiredRetiredSectionView
itemsrequiredarray ofRetiredLeadBriefView
closed_at_msrequiredinteger (uint64)dispositionoptionalstringidrequiredstring (uuid)questionrequiredstringreasonoptionalstring
totalrequiredinteger (uint)
rulesrequired array ofstringsettledrequiredSettledSectionView
confirmed_assertionsrequiredConfirmedAssertionsSectionView
itemsrequiredarray ofConfirmedAssertionBriefView
claimrequiredstringidrequiredstring (uuid)
totalrequiredinteger (uint)
proof_argumentsrequiredProofArgumentsSectionView
itemsrequiredarray ofProofArgumentBriefView
certaintyrequiredstringconclusionrequiredstringidrequiredstring (uuid)questionrequiredstring (uuid)recorded_at_msrequiredinteger (uint64)
totalrequiredinteger (uint)
sinceoptionalstring
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.
settled.listParameters
tree_idstring
Output shape
conclusionsrequiredarray ofSettledConclusionView
certaintyrequiredstringconclusionrequiredstringidrequiredstring (uuid)questionrequiredstring (uuid)question_textrequiredstringrecorded_at_msrequiredinteger (uint64)recorded_byrequiredstring
negative_findingsrequiredarray ofNegativeFindingView
expectationrequiredstringinferencerequiredstringlog_entryrequiredstring (uuid)questionoptionalstring (uuid)recorded_at_msrequiredinteger (uint64)recorded_byrequiredstringsearch_contextoptionalstring
refutationsrequiredarray ofRuledOutAssertionView
claimrequiredstringidrequiredstring (uuid)ruled_out_at_msoptionalinteger (uint64)ruled_out_byoptionalstringstatusrequiredstring
rejected_evidencerequiredarray ofRejectedEvidenceView
assertionrequiredstring (uuid)proof_argumentrequiredstring (uuid)recorded_at_msrequiredinteger (uint64)recorded_byrequiredstringtreatmentrequiredstring
retired_leadsrequiredarray ofRetiredLeadView
closed_at_msrequiredinteger (uint64)dispositionoptionalstringidrequiredstring (uuid)questionrequiredstringreasonoptionalstring
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.
source.getParameters
idrequiredstring (uuid)tree_idstring
Output shape
blobsrequired array ofstringderived_fromoptionalstring (uuid)idrequiredstring (uuid)titlerequiredstring
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).
source.listParameters
cursoroptionalstringlimitoptionalinteger (uint32)tree_idstring
Output shape
itemsrequiredarray ofSourceListItemView
claim_countrequiredinteger (uint32)has_citationrequiredbooleanidrequiredstring (uuid)titlerequiredstring
next_cursoroptionalstring
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.
tree.statsParameters
top_surnamesoptionalinteger (uint32)tree_idstring
Output shape
birth_year_maxoptionalinteger (int32)birth_year_minoptionalinteger (int32)missing_birth_date_countrequiredinteger (uint32)missing_death_date_countrequiredinteger (uint32)person_countrequiredinteger (uint32)top_surnamesrequiredarray ofSurnameCountView
countrequiredinteger (uint32)surnamerequiredstring
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.
redaction.proposeParameters
reasonrequiredstringtarget_eventrequiredstring (uuid)tree_idstring
Output shape
messagerequiredstringproposal_idrequiredstring (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
kindrequiredstring [const: about]valuerequiredPartialDateInput
kind = after
kindrequiredstring [const: after]valuerequiredPartialDateInput
kind = before
kindrequiredstring [const: before]valuerequiredPartialDateInput
kind = between
kindrequiredstring [const: between]valuerequired array ofany
kind = exact
kindrequiredstring [const: exact]valuerequiredPartialDateInput
kind = range
kindrequiredstring [const: range]valuerequired array ofany
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PartialDateInput
calendarrequiredstring [enum: gregorian, julian]dayoptionalinteger (uint8)dual_yearoptionalinteger (int32)monthoptionalinteger (uint8)yearrequiredinteger (int32)
PersonSummaryView
birth_yearoptionalinteger (int32)death_yearoptionalinteger (int32)idrequiredstring (uuid)namerequiredstring
RenderedTextOutput
htmlrequiredstringtextrequiredstring
RenderedTextOutput
htmlrequiredstringtextrequiredstring
RenderedTextOutput
htmlrequiredstringtextrequiredstring
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.
coverage-gap-reviewquestionResearch 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.
draft-proof-argumentquestionrequiredResearch 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.
plan-research-questionquestionrequiredResearch 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.
resume-investigationsinceA 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.
heartwood://methodology/epistemic-statusMIME 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.
heartwood://project/stateMIME 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.
heartwood://research/coverageMIME 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.
heartwood://research/question/{id}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.
heartwood://research/question/{id}/coverageMIME 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.
heartwood://research/question/{id}/logMIME 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.
heartwood://research/question/{id}/planMIME 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.
heartwood://research/questionsMIME type: application/json