Open-source, zero-dependency Model Context Protocol (MCP) server to query Apple Health / HealthKit data (190 metrics) from Claude, ChatGPT, Cursor, OpenClaw, Hermes and any MCP agent.
This is a well-executed health data query server with comprehensive tool coverage, clear naming conventions, and detailed parameter descriptions. All 13 tools follow consistent verb_noun naming patterns (get_, list_, query_). Tool descriptions are substantive and actionable (range 100-400+ chars), explaining both WHAT the tool does and WHEN to call it. Input schemas are complete with proper type definitions and enum constraints. A significant strength is the detailed inline documentation in descriptions that guide LLM behavior (e.g., 'Call this first to discover metric names', 'check the coverage block before trusting long windows'). Output schemas are well-documented in descriptions. Error handling includes recovery guidance in descriptions. Security is well-managed: read-only operations, no credentials in parameters, local-first design. The main gap is that output schemas are described in prose within descriptions rather than formally declared as separate outputSchema properties in the tool definitions (this is not strictly required by MCP but is a best practice). Tool composition is excellent, each tool has a single clear responsibility, and the tools chain naturally (e.g., list_metrics → get_health_metrics with metric name). Three tools include pagination support via cursor parameters. Annotations (readOnlyHint, idempotentHint, openWorldHint) are properly declared once and applied globally.
Compare a metric between two arbitrary date periods (A vs B): each aggregate plus the change and percent change. Pass periodA/periodB explicitly, or pass anchor {eventId, days} to build both periods around a logged event (the before/after question, with the event day excluded from both sides).
Get values for a metric (or all metrics) over an optional date range, with an aggregate (avg/sum/min/max/latest). The core data-retrieval tool. Every result carries a `coverage` block giving the metric's real firstDate/lastDate/days: check it before trusting a long window, and note that `aggregate` is always computed over the full range even when `points` are rolled up. Single-metric answers also list any logged point events inside the window as segmentBoundaries.
The current hour-by-hour window from the iOS app's HOURLY automations (health-intraday.json, app 1.4+): each metric's hourly points plus its latest value. The file is REPLACED on every hourly run, so this is a live within-day view, not history; use get_health_metrics for day-level questions. Returns available:false with setup guidance when no hourly automation has delivered yet.
Health check: data source, how many metrics/workouts are available, which optional context files exist, and the most recent data date. Call this first to confirm the bridge is connected.
Output schemas are documented in prose descriptions rather than as formal outputSchema JSON Schema declarations
compare_periods accepts both explicit periodA/periodB AND anchor object, creating optional parameter interdependency that could confuse LLMs if not carefully enforced server-side
get_health_metrics filterDays parameter has complex nested object structure with conditional logic (eventType/eventTag/negate) that would benefit from explicit statement in description: 'Pass either eventType or eventTag (or both), combined with optional negate flag'
Inferred effective spec: 2025-06-18+.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | B | 74 | 2025-06-18+ | v2 |
User-logged menstrual cycle starts from the iOS app (1.5+): observed only, never predictive. Each entry is a single day the user marked. Optionally filter by date range.
Per-field opt-in context the user chose to share: age, sex, height, activity level, medical conditions, medications (logged separately in events). Absence of a field means it was withheld or never enabled, not that it is empty. Returns available:false when the iOS app (1.5+) has not exported a profile file.
Clustered sleep sessions from the iOS app (1.5+): each with start/end times, waking day, duration, and stage breakdown (core/deep/REM/awake). Optionally filter by a specific day or date range. Sessions are attributed to the WAKING day.
Return clean structured JSON for the chosen metrics/date range. Paginated: the result carries `nextCursor` when more metrics remain; pass it back as `cursor` for the next page. Prefer naming the metrics you need and a date range; calling it bare over a full history is a lot of data.
Compare the most recent N-day window against the prior N days for a metric: change, percent change and direction (up/down/flat). Also returns `daysAvailable` and `windowSatisfied`: if windowSatisfied is false the file does not hold enough history for the window you asked for, and the comparison is over less data than requested. Logged point events inside the compared span are listed as segmentBoundaries.
List logged health events (medication, habit, visit, shift, episode, travel, etc.) with optional filtering by type, tag, or date range. Returns available:false when the iOS app (1.5+) has not yet exported an events file.
List every available Apple Health metric with its unit, day count, and date range. Use this to discover metric names before querying.
List every recorded workout: name, date, duration, distance, elevation, average/max heart rate, power (running/cycling), cadence, and more. Workouts include intervals/laps when recorded.
Natural-language convenience: pass a question and get routed structured results. Prefer the specific tools above when you can, and call list_metrics first to see how much history exists, since this tool answers over whatever the file holds.
query_health_data is a natural-language convenience wrapper that duplicates functionality of more specific tools (get_health_metrics, get_trends, etc.). While useful for LLM UX, it creates tool selection ambiguity and increases reasoning overhead