Skip to content

Recording data right

When you ask the agent to record or import health data, it should always look the data up in the HDS data model first, then write events matching the canonical shape:

1. search_items("basal body temperature")
→ top match: body-temperature-basal
2. get_item("body-temperature-basal")
→ streamId: "body-temperature-basal"
eventType: "temperature/c"
type: "number"
3. import_batch([
{ streamIds: ["body-temperature-basal"],
type: "temperature/c",
content: 36.6,
time: <epoch-seconds> },
...
])

Skipping this lookup and inventing stream and type names breaks the cross-system mapping (HealthKit / FHIR / SNOMED CT / ICF) that HDS items carry. The MCP’s tool descriptions explicitly tell the agent to call search_items first; if no item matches, the agent should ask you before inventing a type.

When you bring a dataset that HDS has no item for yet — an export from an app we don’t bridge, a spreadsheet of measurements, a tracker’s CSV — the agent must not dump it into note/txt events if the information can be structured. Notes are opaque text: they can’t be charted, converted, compared across sources, or mapped to HealthKit/FHIR/SNOMED. A note is a dead end; a structured event keeps its meaning everywhere.

The expected investigation order:

  1. Search the item catalogue (search_items, or browse model-browser.datasafe.dev) — most common observations already have a canonical item.
  2. Check the event-type catalogue (pack.json eventTypes) for a fitting category/unit type (e.g. mass/kg, temperature/c, count/generic) when no full item matches.
  3. Ask before inventing. If neither matches, the agent should surface the gap to you — and you’re welcome to open an issue so the data model team can add a proper item.
  4. Only genuinely free-form content (a diary entry, a doctor’s letter text) belongs in note/txt.

If your agent reaches for notes as the easy way out, push back — and expect it to push back on you, too.

Bulk imports: safe to run twice, easy to remove

Section titled “Bulk imports: safe to run twice, easy to remove”

An import of an archive (letters, lab reports, another app’s export) will be run again: the source grows, a mapping gets fixed. It must update what it wrote rather than duplicate it, and the whole import must be findable and removable. The contract (details and code in llms-full.txt §5f):

  1. Every imported event carries clientData.sourceKey, written <tool>:<kind>:<id>: the importer’s name, the kind of record, and the record’s own identity in the source, the same on every run.
  2. The import has its own access, so each event’s author names it.
  3. That access is scoped: manage on exactly the root streams it writes, never on everything (*).
  4. Sources first: the document or the provider is written before the values that cite it.
  5. A re-run updates in place by sourceKey, and removing the import is one query on the sourceKey prefix (<tool>:) followed by deleting what it returns.

Dates known only to the month or the year (“March 1999”), and the range and flag a lab printed beside a value, have their own conventions in the data model: PARTIAL-DATES.md and BLOOD-CHEMISTRY.md.

Worked pilot — fertility-charts folder → HDS

Section titled “Worked pilot — fertility-charts folder → HDS”

You drop a folder of Excel sheets and scanned paper from fertility charting into your machine and ask your agent to upload it to a demo HDS account.

“Sign me in to HDS demo, then process the folder at ~/fertility-2026/ and import every reading.”

The agent:

  1. Calls hds.connect() → you sign in to demo.
  2. Reads the folder via its filesystem tool.
  3. Uses vision to OCR scanned BBT sheets; uses code interpreter to parse the Excel.
  4. For each distinct data type (BBT, cervical fluid, LH test, …) calls hds.search_items(...) then hds.get_item(...).
  5. Calls hds.import_batch(...) with the canonical streamIds + eventType from the item spec.
  6. Confirms with hds.list_streams() / hds.get_events(...).