← MetricBridge

Apple Health MCP server

The MetricBridge MCP server exposes your Apple Health data to any MCP client as 14 read-only tools. It is zero-dependency, runs entirely on your own machine, and never writes to Apple Health or sends data anywhere.

Last updated 24 July 2026 · source on GitHub · npm: health-export-mcp

On this page

What is an Apple Health MCP server?

An Apple Health MCP server is a Model Context Protocol server that exposes your Apple Health data to AI agents as tools they can call on demand, rather than as a file you paste into a chat. MetricBridge's server reads a local JSON snapshot written by the iOS app and serves it through 14 read-only tools, so an agent can ask for a specific metric, trend or date range instead of loading everything into its context.

The practical difference: Apple's own export produces one large export.xml archive that most tools cannot parse and that will not fit in a model's context window. An MCP server turns the same data into queryable tools.

Install

Before you start. This server reads a file. It does not talk to your iPhone directly. You need three things: the MetricBridge iOS app (it writes the JSON export), a destination you point it at (iCloud Drive, a local folder, your LAN or a webhook), and Node.js 18+ on the machine running your agent. Without the app there is no export for this server to read.

The server is a single JavaScript file with no dependencies. It requires Node.js 18 or later.

npx health-export-mcp

Or install it globally:

npm install -g health-export-mcp

You can also run the file directly from a clone of the repository; it imports nothing outside Node's standard library, so there is no install step to audit.

Where the data comes from. The iOS app writes .health-cache.json to a location you choose. On macOS the default is ~/Library/Mobile Documents/iCloud~ai~healthexport~app/Documents. Point HEALTH_DATA_DIR at that folder (see environment variables).

The fourteen tools

All eight are read-only. None of them can write to Apple Health, and none of them make network requests.

get_mcp_status

Health check: reports the data source, how many metrics and workouts are available, and the most recent data date. Call this first to confirm the bridge is connected.

{ "ok": true, "source": "file /Users/you/…/.health-cache.json",
  "metricCount": 34, "workoutCount": 12, "lastDataDate": "2026-07-24" }

list_metrics

Lists every available metric with its unit, day count and date range. Use it to discover exact metric names before querying: names are snake_case, such as heart_rate_variability.

get_health_metrics

The core data-retrieval tool. Returns daily values for one metric (or all metrics) over an optional date range, plus an aggregate.

ParameterTypeNotes
metricstringe.g. step_count. Omit for all metrics.
start, endstringYYYY-MM-DD, both optional.
aggregationstringavg, sum, min, max or latest. Defaults to sum for cumulative metrics, average otherwise.

Compares the most recent N-day window against the prior N days and returns the change, percent change and direction. This is the tool behind questions like "compare my HRV this week versus last week".

ParameterTypeNotes
metricstringRequired.
windowintegerDays per window. Defaults to 7.
{ "metric": "heart_rate_variability", "unit": "ms", "window": 7,
  "recent": 59.657, "prior": 57.157, "change": 2.5,
  "changePercent": 4.374, "direction": "up" }

compare_periods

Compares one metric between two arbitrary date periods and returns each aggregate plus the change and percent change. Use it when the two windows are not adjacent, for example this January against last January.

get_structured_export

Returns clean structured JSON for the chosen metrics and date range, shaped to drop straight into an agent's context window.

get_intraday

The current hour-by-hour window written by the iOS app’s hourly automations (app 1.4 or later) to health-intraday.json, beside the daily cache. The file is replaced on every hourly run (a live within-day view, not an accumulating history), so day-level questions still belong to get_health_metrics. Optional metric filters to one metric; when no hourly automation has delivered yet it answers available: false with setup guidance instead of an error.

{ "available": true, "file": "health-intraday.json", "replacedEachRun": true,
  "metrics": [ { "name": "heart_rate", "unit": "count/min",
                 "points": [ { "t": "2026-08-17 09:00:00 +0100", "v": 71 } ],
                 "latest": { "t": "2026-08-17 09:00:00 +0100", "v": 71 } } ] }

query_health_data

A natural-language convenience tool: pass a question such as "average HRV last month" and it routes to structured results. Prefer the specific tools above when you know which one you need. They are cheaper and more predictable.

list_events

The user's event timeline from health-events.json: medication starts, habit changes, doctor visits, shift blocks, trips. Filter by type, tag or date range. Events are typed by the user in the app (1.5 or later) and are what make before-and-after analysis honest.

get_workouts

Workouts with per-interval structure and running dynamics (power, stride length, vertical oscillation, ground contact time) where recorded. Filters for activityType and date range, paginated with limit and cursor.

get_profile

The opt-in agent profile (conditions, medications, goals, allergies, notes). A field the user did not enable is absent, and the answer says absence means withheld, never none.

get_sleep_sessions

Individual sleep sessions with local time offsets, attributed to the waking day the same way the daily cache is, so the two never disagree. Split nights appear as separate sessions on one day.

get_cycle_context

Day-in-cycle and coarse phase derived from user-logged periods only. Every answer states it is derived from logged data and is not predictive. Only present when the user opted in to sensitive categories.

correlate_metrics

Aligns two metrics day by day with an optional lag of up to 3 days (alcohol tonight against HRV tomorrow), returns the aligned-pair count and Pearson r, and refuses to correlate on fewer than 10 aligned days. The payload itself says: association, not causation.

Prompts, demo mode and the CLI

The server also ships 22 MCP prompts (daily brief, doctor visit prep, an /experiment protocol, shift block comparisons and more), a --demo mode that serves a watermarked synthetic dataset so you can try every tool before exporting anything, a --doctor diagnostics command, and status --max-age <hours> as a freshness gate for cron jobs.

Client configuration

Any MCP client works. Replace SERVER_PATH with the path to server.mjs and HEALTH_DATA_DIR with your export folder.

Since 1.4.0 the server is a set of small modules (server.mjs, healthstore.mjs, events.mjs, prompts.mjs, demo.mjs, receiver.mjs). The easiest installs are npx health-export-mcp or the one-click bundle; if you download files by hand, keep them together in one folder.

Claude Desktop

Edit claude_desktop_config.json and merge into mcpServers, then restart Claude Desktop.

{
  "mcpServers": {
    "health-export": {
      "command": "node",
      "args": ["SERVER_PATH"],
      "env": { "HEALTH_DATA_DIR": "/path/to/your/export/folder" }
    }
  }
}

Claude Code

Use the CLI. It writes the config for you:

claude mcp add health-export -e HEALTH_DATA_DIR="/path/to/folder" -- node "SERVER_PATH"

Claude Code also reads a project-scoped .mcp.json using the same mcpServers shape as Claude Desktop. If pairing is enabled, do not pass PAIRING_SECRET on the command line: it would land in your shell history and in ps output. Put it in the config file instead.

Cursor

Cursor reads ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project), using the same mcpServers shape shown above.

VS Code

VS Code's MCP support reads .vscode/mcp.json in the workspace, again with the same server shape. Reload the window after editing.

opencode

Add the server to opencode.json:

{
  "mcp": {
    "health-export": {
      "type": "local",
      "command": ["node", "SERVER_PATH"],
      "enabled": true,
      "environment": { "HEALTH_DATA_DIR": "/path/to/folder" }
    }
  }
}

Any other MCP client

The server speaks standard MCP over stdio, so any compliant client works: OpenClaw, Hermes, and others. Point the client at node SERVER_PATH and set HEALTH_DATA_DIR.

Environment variables

VariablePurpose
HEALTH_DATA_DIRFolder containing .health-cache.json. Defaults to the current directory; on macOS the app's iCloud container is ~/Library/Mobile Documents/iCloud~ai~healthexport~app/Documents. A leading ~ is expanded.
PAIRING_SECRETOnly needed if the iOS app has pairing enabled. Must match the code shown in the app under Settings → Agent pairing. Without it the data tools return a clear "Locked" error rather than an empty result.

Transports (stdio only)

stdio is the only MCP transport. Every client config above uses it, and there is no hosted endpoint. The server only ever runs on your machine, reading a local file.

Verifying the download

Every published artifact is checksummed and signed, so you can confirm you are running exactly what was published before you execute it.

minisign -Vm SHA256SUMS -p minisign.pub
shasum -a 256 -c SHA256SUMS

Is it private?

Yes. The server makes no network requests, has no dependencies, requires no account, and reads a local file you control. Apple Health access happens only in the iOS app, which requests read-only HealthKit permission and never writes back. Your data goes only to the destination you configure.

MetricBridge · local-first, read-only. · Home · Privacy · Terms · Support · Security