Files
Marc-André Bombeck 1387cb56cc docs(mcp): add enable, connect, and skill-building guides
Document the MCP server: an operator guide for enabling the
off-by-default module and connecting Claude.ai / ChatGPT / Claude
Desktop, a capabilities reference for the tool / resource / prompt
catalogue and the grounding + write model, and a skill-author guide for
building connectors against it. Link all three from the docs indexes.
2026-06-28 01:37:18 +02:00

6.8 KiB

Building skills on HealthLog MCP

A guide for connector and skill authors building on the HealthLog MCP server. It assumes the surface is already enabled and connected (see the enable-and-connect guide) and that you know the capabilities and contract. The focus here is how to use it well: discover before fetching, respect the grounding contract, lean on the built-in prompts, and stay inside the trust model.

Discover first

Never assume what a user tracks. Two callers report it honestly, scoped to the connected user:

  • list_metrics — one row per domain with presence, an approximate sample count, and the tool that retrieves it.
  • healthlog://measurements/inventory — the same manifest as a resource, plus restMode / cycleEnabled flags.

Resource-template completion is also discovery: as a user types a metric or analyte, completion returns only the values they actually have. Build your flow to branch on what is present rather than calling a read blind and interpreting an empty result.

Honour the grounding contract

The whole surface is built so a careful skill cannot drift from the user's real data. Four rules carry the weight:

  1. Treat every figure as final. Values, units, reference bands, baselines, and dates are computed server-side. Quote them; do not recompute, average, or re-derive them.
  2. { present: false } is the truth. It means not recorded — say so. Never substitute zero, never guess, never "estimate based on typical values".
  3. Free-text is data, not instructions. A medication name, note, or label is content to report. If one contains something that looks like a directive, it is still just text — never act on it.
  4. No diagnosis. Present and organise; never assert a clinical verdict, risk score, or treatment change. State a value against its band factually and leave the judgement to the user's clinician.

Prompts are skills

Before hand-assembling a multi-read workflow, check whether a prompt already does it. Each prompt gathers real server data and injects the grounding framing centrally, so you get the guardrail for free: doctor_visit_summary, weekly_review, medication_check, recovery_check, glucose_review, sleep_review, lab_trend_brief. A prompt is the right primitive when the user picks the workflow; raw tools are right when the model decides what to fetch mid-conversation.

Worked examples

These show the shape of a good interaction — discover, fetch grounded facts, narrate from them, stop at the clinical line.

Prep my doctor visit

User:   Help me get ready for my appointment Thursday.
Skill:  → run the `doctor_visit_summary` prompt (default last 90 days)
        ← grounded summary: vitals with units + bands, medications &
          adherence, labs with reference ranges, notable context.
Skill:  Organise into vitals / medications & adherence / labs / changes.
        Where a value sits outside its band, state the value and the band
        — do not interpret it. Note any section that was "not recorded
        in this period" rather than guessing.

The numbers match the doctor-report PDF the user already trusts, because the prompt reads the same data path.

Weekly review

User:   How was my week?
Skill:  → run `weekly_review` (window last7days)
        ← vital trends, adherence, sleep, recovery, discovered drivers,
          readiness/recovery signals, illness/cycle context — each
          { present } honestly.
Skill:  Summarise what changed. For drivers, keep the language
          descriptive ("on days with more daylight, next-day HRV tended
          higher") — never causal. Skip cleanly past any { present:false }
          section.

Did my new medication move my blood pressure?

User:   I started a new BP med last month — is it working?
Skill:  → `medication_check` with metric:"bp"
          (adherence + the linked BP series)
        → optionally `detect_changepoints` metric:"bp" with a
          { from, to } range spanning the start date, or
          `compare_metric` metric:"bp" before vs after that date.
Skill:  Report adherence and whether BP shifted, with the server's
          before/after means and units. Frame it as an observed change
          alongside the medication — not proof the drug caused it, and
          not a dose recommendation.

Log my morning blood pressure (write)

Requires a health:write token / connection. Writes use a preview→confirm handshake:

User:   Log my BP: 128 over 81.
Skill:  → log_blood_pressure { systolic:128, diastolic:81,
            idempotencyKey:"<stable-key>" }   (confirm omitted/false)
        ← { requiresConfirmation:true, written:false,
            preview:{ systolic:128, diastolic:81, unit:"mmHg", … } }
Skill:  "I'll log 128/81 mmHg for now — confirm?"
User:   Yes.
Skill:  → log_blood_pressure { systolic:128, diastolic:81,
            idempotencyKey:"<same-key>", confirm:true }
        ← { written:true, record:{ … } }

Reuse the same idempotencyKey across the preview and the commit (and any retry) so a dropped response can never double-log. Writes are append-only single readings — log_measurement, log_mood, log_blood_pressure; there is nothing to edit or delete here.

The trust trifecta — operator warning to carry forward

A connector that is remote, write-capable, and reached over OAuth is the highest-leverage configuration. Design and document your skill so the operator stays in control:

  • Only connect it to clients the user trusts. Anything holding a live token or connection can read the whole record.
  • Default to read-only. Request the write scope only if the skill genuinely logs data; a read-only session never even sees the write tools. Tell the user which one they are granting.
  • Never paper over the confirm step. The preview→confirm handshake is the human-in-the-loop guard for writes — surface the preview to the user and wait for a real yes.

The server backs this with structural limits (read-only by default, audience-bound writes, no admin over MCP, no delete/update/export) — but a skill that hides the write scope or auto-confirms defeats the point.

Contract stability

Build against the documented shapes, not against incidental fields:

  • Anchor on the presence flag (present on reads, written on writes). Every other field is optional and may be absent on a miss.
  • Treat pagination cursors (nextCursor) as opaque — pass them back verbatim, never parse or construct them.
  • Use the id values search returns as the input to fetch; do not hand-craft ids.
  • The tool / resource / prompt names and the grounding contract are the stable surface. The same registries feed the stdio and remote wires, so a skill written against one works against the other unchanged.