Skip to main content
Glama

Hearth

Hearth is a self-hosted MCP server that lets a family look after an aging parent by voice: medication check-ins, appointments, a daily summary, missed-dose escalation, and sharing that the parent controls. It ships with voice-sim, an MIT-licensed voice harness that drives any MCP server through an LLM and scores it against a golden eval.

Architecture

┌──────────────────────── voice-sim (MIT, generic) ─────────────────────────┐
│  Browser: index.html + app.js        Agent server (Node, 127.0.0.1 only)  │
│  ─ mic / TTS (Web Speech) ◄──SSE──►  ─ LLM provider: Gemini | Groq | Mock │
│  ─ push-to-talk, text fallback       ─ MCP client (Streamable HTTP)       │
│  ─ profile switch (id/label/emoji)   ─ confirmation gate (FR-HARN-2)      │
│  ─ trace list                        ─ headless `eval` CLI                │
└───────────────────────────────────────────────┬───────────────────────────┘
                                                │ POST/GET/DELETE /mcp (SSE)
                                                │ Authorization: Bearer
┌──────────────────────── hearth-mcp ────────────▼──────────────────────────┐
│ node:http ─► guard (Host/Origin/public-mode) ─► auth → Principal ─►       │
│ session router (404/403) ─► per-session McpServer (role tools only) ─►    │
│ handlers (strict zod in-handler) ─► domain services ─► db.tx ─► SQLite    │
│ Scheduler evaluate(now): boot / 60 s / clock routes · Clock (injected)    │
│ pino (redacted) · /healthz · /dev/* (gated)                               │
└───────────────────────────────────────────────────────────────────────────┘

MCP spec 2025-11-25 on @modelcontextprotocol/sdk@1.32.1, TypeScript on Node ≥ 22.16, SQLite through node:sqlite. The full specification is docs/SPEC.md.

Related MCP server: Hearth v2

Quick start

  1. Run it. Docker Compose v2.24 or newer is the only requirement:

    docker compose up

    The first run builds the image. The server seeds a synthetic "Shah family" on first boot. Both ports are published on 127.0.0.1 only:

    • Hearth MCP server: http://127.0.0.1:8787/mcp (health check: http://127.0.0.1:8787/healthz)

    • voice-sim: open http://localhost:5173 in Chrome

  2. Add an LLM key (optional). voice-sim needs one to answer; the MCP server does not.

    cp .env.example .env    # then set GEMINI_API_KEY= (and optionally GROQ_API_KEY= as a fallback)
    docker compose up

    .env is git-ignored and is read only at runtime. Keys never reach the browser. For the snappiest voice, set LLM_PROVIDERS=groq,gemini and LLM_RPM=30 in .env: on Groq a turn answers in about 2.5 s (FL-16).

  3. Connect MCP Inspector. Run npx @modelcontextprotocol/inspector@2.11.0, choose Streamable HTTP, use http://127.0.0.1:8787/mcp, and add the header Authorization: Bearer demo_caregiver (or demo_parent). The full checklist is in docs/inspector.md.

Metrics

ID

Metric

Target

Measured

Source

M1

Tool routing on the 24-item golden set (gemini-3.5-flash-lite)

≥ 22/24

24/24

eval/results/latest.json

M2a

Confirmation compliance on the golden set

24/24

24/24

eval/results/latest.json

M2b

Tier ≥ 2 commits without an elicitation in the audit log

0

0

npm run eval:mock (CI)

M3

Final spoken replies of 25 words or fewer

≥ 23/24

24/24

eval/results/latest.json

M4

Tool latency p95, excluding time spent waiting on the human

≤ 300 ms

51 ms

eval/results/latency.txt

M5

Median end of speech → start of spoken reply, 10 voice turns

≤ 3.5 s

2.56 s (Groq gpt-oss-120b first, Chrome's recogniser)

scripts/voice-timing.mjs, 2026-10-21

M6

Inspector lists the 11 caregiver tools and completes a confirmed write

pass

pass (7/7 checks)

docs/inspector.md

M7

Fresh clone → docker compose up → Inspector connected / first voice answer

≤ 5 min each

4 min 24 s / 48 s

timed run, 2026-10-20 (cold build 187 s)

CI runs typecheck, lint, the test suites, the keyless mock eval, the M1–M3 threshold check and a secret scan on every push (.github/workflows/ci.yml).

Screenshots

MCP Inspector connected as the caregiver, with an elicitation-confirmed write:

MCP Inspector

voice-sim, push-to-talk with the tool trace:

voice-sim

Roles & tools

Each MCP session gets only its role's tools, so an out-of-role call is simply "tool not found". Writes are tiered: T0 reads, T1 low-risk writes without confirmation, T2/T3 writes confirmed by the user through elicitation.

Tool

Parent (Mom)

Caregiver (Arnav)

Tier

get_daily_summary, list_medications, get_appointments, get_status_help

✓

✓

T0

get_pending_messages

✓

✓

T1

log_dose

✓

T1

get_sharing

✓

T0

set_sharing

✓

T2

get_alerts

✓

T0

ack_alert

✓

T1 (T2 when clearing several)

upsert_medication, upsert_appointment, send_check_in

✓

T2

cancel_appointment

✓

T3

The parent gets 8 tools and the caregiver gets 11. Demo bearer tokens are demo_parent and demo_caregiver.

Trust boundary

Confirmation is enforced by the server, not by the LLM or the client. Every T2/T3 write asks the human through MCP elicitation, reads back exactly what will change, and re-validates the plan before committing. It follows that a client without elicitation support cannot run T2/T3 tools; they fail closed. A confirm-token fallback for such clients is future work (decision D8 in the spec).

Privacy

  • All seed data is synthetic: the "Shah family", its medications and its appointments are invented.

  • Free-tier LLM providers may retain prompts for training or review. Don't put real health data through voice-sim with a free-tier key.

  • With a Groq key, push-to-talk audio goes to Groq for transcription (Whisper). Set VOICE_SIM_STT=browser to keep speech recognition in Chrome instead.

  • API keys and bearer tokens stay in .env and the server process. They never reach the browser, the event stream, logs or eval reports, and CI fails on a secret-pattern scan.

  • Error logs carry the error class, code and stack, never the message, which can echo user input.

Tunnels

The server binds 127.0.0.1 by default. To reach it through a tunnel:

  • Add the exact tunnel hostname to HEARTH_ALLOWED_HOSTS. Never rewrite the Host header to get past the check.

  • A non-loopback allowed host or any forwarding header (cf-ray, x-forwarded-for, forwarded, …) switches on public mode: /dev/* returns 404 and the demo_ tokens are refused with 401.

  • Mint real tokens with npm run seed -w packages/hearth-mcp -- --public; they are printed once. In public mode the server never seeds an empty database itself, since nobody would see its tokens; it logs this command instead.

  • voice-sim is never tunnelled. It holds your LLM keys and listens on loopback only.

voice-sim as a standalone MCP harness

packages/voice-sim is generic and MIT-licensed: it contains nothing Hearth-specific and works with any Streamable HTTP MCP server.

  • Profiles. profiles.json lists who can talk, as [{ "id", "label", "emoji", "headers" }]. Headers (for example an Authorization bearer) stay on the agent server; the browser sees only id, label and emoji. Point VOICE_SIM_PROFILES at the file and VOICE_SIM_MCP_URL at your server, then run npm run serve -w packages/voice-sim. VOICE_SIM_LANG sets the speech recognition language (Compose uses en-IN; empty means the browser's language).

  • Speech to text. With GROQ_API_KEY set, push-to-talk audio is recorded in the browser and transcribed on the agent server by Whisper (GROQ_STT_MODEL, default whisper-large-v3-turbo), which is far more accurate than Chrome's recogniser for accented English. A clip without voice in it is never sent, because Whisper invents words from silence (FL-18). Without a Groq key, or with VOICE_SIM_STT=browser, Chrome's recogniser is used.

  • Providers. Gemini, Groq and a scripted Mock, tried in LLM_PROVIDERS order with retry, backoff and a per-minute throttle. A turn is capped at 4 tool calls and 15 s, excluding waits on the human and on the throttle.

  • Eval CLI. voice-sim eval --server URL --file golden.jsonl --profiles P --dev-token T [--provider gemini|groq|chain|mock] runs a JSONL golden set and scores routing, confirmation compliance, brevity and final status into JSON and Markdown reports. Hearth's set is eval/golden.jsonl.

License

MIT. See LICENSE.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      Enables eldercare coordination through medication reminders with safety guardrails, daily voice check-ins with symptom escalation, and a family caregiver dashboard.
      8
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables an older adult to talk with Alexa+ about medicines and daily routine while a caregiver sees a calm, consent-gated dashboard and push alerts only when a genuine pattern (e.g. an unusually late first activity) appears. Includes elicitation-based consent, emergency bypass, resumable SSE streams, and an inline MCP App view.
      1
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables an ambient voice companion to converse with isolated older people while exposing MCP tools and app cards for long-term memory, background reflection, reminders, two-way family messages, digests, and emergency escalation over Streamable HTTP. Family members get honest impressions and alerts, and emergencies reach simulated (or pluggable real) dispatch before loved ones are notified.
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables Alexa+ and other MCP clients to maintain long-term elder-care memory, deliver proactive medication reminders, run daily check-ins, generate doctor-visit briefs and dashboards, and autonomously alert family members based on risk signals.
      MIT