mibly-apple-health-mcp
Provides access to Apple Health (HealthKit) data, including sleep, steps, heart rate, HRV, workouts, weight, body fat, and more, for use by AI agents.

Apple Health MCP — Mibly Receiver
An open-source Apple Health MCP server: bring your Apple Health (HealthKit) data — sleep, steps, heart rate, HRV, workouts, weight, body fat, and more — into Claude Desktop/Code, Codex, or any other AI agent you build or run yourself (anything that speaks MCP).
Local mode: your data never touches Mibly's servers. No account, no cloud, no telemetry. It is stored encrypted on a machine you control, and this repository is the code that does it.
Quickest way to get started
Download Mibly on your iPhone → App Store
Copy the setup prompt from inside the Mibly app (Connection Settings → "Copy for your Agent") and paste it into your agent.
Your agent sets up the receiver and MCP service for you. No cloning, no building, no config files.
Related MCP server: Health4AI
Why an iPhone app?
Apple Health data can only be read through HealthKit, inside a native iOS app. There is no web API. Mibly handles the HealthKit side — permissions, background sync, and data formatting — so you don't need an Apple Developer account or Xcode.
What this repository is
A reference implementation of the receiver your agent installs. It's here so you can:
Review exactly what runs on your machine and where your data goes
Customize it if you want to
You don't need this code to use Mibly. The setup prompt above is the recommended path.
Where does the data go?
Question | Answer |
Where is health data stored? |
|
Does the service make outbound network requests? | No. It only listens. No telemetry, no update check. |
Who can read it over the network? | Anyone holding the pairing token. Every route requires |
How long is it kept? | 365 days of history. Only the latest sample per type is kept longer. |
Is traffic encrypted in transit? | No. Local mode is designed for your home or office LAN: the iPhone and this receiver sit on the same Wi-Fi and talk plain HTTP. Don't expose the port to the public internet. |
Check it yourself. This should print nothing:
grep -rnE "fetch\(|https?\.request|axios|net\.connect|XMLHttpRequest" srcThe wire protocol (endpoints, payloads, storage format) is in PROTOCOL.md; the threat model is in SECURITY.md.
Run it yourself
Requires Node.js 18+.
npm ci
npm run build
MIBLY_TOKEN=<token from the Mibly app> npm startOn first run the token is saved to ~/.mibly/config.json (mode 0600), so later restarts need no env var. On startup the receiver prints the address(es) to use, e.g. Target Host to enter in Mibly: 192.168.1.24:7345 (en0). Enter that in the app's Target Host field (just IP:port, no http://, no path); pick the address on your Wi-Fi/LAN if several are listed.
Variable | Meaning | Default |
| Pairing token from the app | saved config |
| Bind address |
|
Connect an MCP client over HTTP to http://<ip>:7345/mcp with the header Authorization: Bearer <token>. For agents that spawn MCP servers over stdio, add --stdio (logs go to stderr).
If you regenerate the token in the app, restart with the new MIBLY_TOKEN. The old encrypted store can't be decrypted with the new key, so the receiver starts empty.
Code map: src/index.ts (routes) · src/auth.ts (token check) · src/config.ts (~/.mibly/config.json) · src/ingest.ts (validation) · src/store.ts (encryption, retention, queries) · src/mcp.ts (the two tools). Run the tests with npm test.
Start automatically on boot
The receiver should keep running after a restart. Replace the paths (and, on Linux, the user) with yours; the token is already saved in ~/.mibly/config.json, so no env var is needed.
macOS (launchd) — ~/Library/LaunchAgents/com.mibly.receiver.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.mibly.receiver</string>
<key>ProgramArguments</key><array>
<string>/usr/local/bin/node</string>
<string>/Users/you/mibly-apple-health-mcp/dist/index.js</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict></plist>launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.mibly.receiver.plistLinux (systemd) — /etc/systemd/system/mibly-receiver.service:
[Unit]
Description=Mibly receiver
After=network-online.target
Wants=network-online.target
[Service]
User=you
ExecStart=/usr/bin/node /home/you/mibly-apple-health-mcp/dist/index.js
Restart=always
[Install]
WantedBy=multi-user.targetsudo systemctl enable --now mibly-receiverRestart the machine, then confirm the receiver is back (use the IP the receiver printed):
curl -H "Authorization: Bearer <token>" http://<ip>:7345/statusA JSON reply with version and lastPushAt means it is running.
Upgrading from an earlier version
Update in place; do not set up a second receiver.
Pull the new code, then
npm ci && npm run build, and restart the service.Keep your token and data. Do not delete
~/.miblyand do not change the token. A new token starts an empty store.Legacy
heartRaterecords (single readings withoutheartRateSummary) are deleted on start-up, becauseheartRateis now an hourly summary. Everything else is kept.Check
GET /status: it should show the newversionandcapabilities.
Newer Mibly app features need the matching receiver version; older apps keep working with a newer receiver.
Local vs Cloud
Local (Free) | Cloud (Pro) | |
Data path | iPhone → an agent environment you control | iPhone → Mibly Cloud → your agents |
Remote access across devices | — | ✓ |
Multiple agents on the same data | — | ✓ |
Menstrual cycle data | Available (off by default) | Never uploaded |
This repository covers Local only. You choose which data types are shared and can turn each one off at any time.
Notes
Mibly does not diagnose or make medical decisions. It only gives your agent access to the data you choose to share.
Issues in this repo are for the receiver code. For app support, see mibly.app.
Privacy policy: https://mibly.app/privacy/
License
MIT
Available Tools
2 toolshealth_detailApple Health detail queryB
Detailed time-range query for one health data type. Limited to the last 365 days.
Health data consumption and presentation (Local and Cloud):
Time/timezone: sleepStart, sleepEnd, start and end may be ISO 8601 UTC timestamps ending in Z. Preserve raw timestamps for storage and calculations. When presenting clock times, convert UTC to the user's local timezone when that timezone is known. Do not display UTC clock times as though they were local. Do not assume the Agent host timezone: a VPS or server may use a different timezone. If the user's timezone is unknown, explicitly label UTC times as UTC or ask for their timezone.
Sleep date: date is already the wake-up calendar date calculated by Mibly using the iPhone's local calendar. Treat date as authoritative for the sleep record. Do not recompute or change date from sleepStart/sleepEnd UTC timestamps.
Sleep minutes: durations and stage values may contain fractional minutes. Preserve raw fractional values for calculations. When presenting whole minutes, round to the nearest minute; do not truncate (for example, 59.6 minutes displays as 60 minutes).
Missing sleep stages: null means stage data is unavailable. Never interpret null as 0. Never invent or infer a missing sleep stage.
Body Fat: bodyFat.value is already a human percentage: 23.5 means 23.5%. Do not divide by 100 or multiply by 100 when presenting it.
Heart rate: a heartRate sample is an hourly summary, not a single reading. value is that hour's average bpm; heartRateSummary has min, avg, max, count and context. context is sedentary (still), active (moving) or unknown (Apple Health did not label it); the same hour can appear once per context. The newest hour may still be in progress and is replaced when updated. Older samples may be single readings without heartRateSummary.
Resting heart rate: restingHeartRate and walkingHeartRateAverage are separate daily values from Apple Health (one per day, bpm). Use restingHeartRate for the user's resting heart rate; never derive it from heartRate minimums or sedentary averages, and do not compare active hours with sedentary hours as if they were the same kind of reading. These are consumption/presentation rules only; preserve the raw Health data and existing calculations.
| Name | Required | Description | Default |
|---|---|---|---|
| end | Yes | ISO 8601 end of the range. | |
| type | Yes | Which health data type to query. Sleep summaries use wake-up dates and nullable stage minutes; bodyFat values are percentages, not fractions. | |
| start | Yes | ISO 8601 start of the range. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the full behavioral burden. It discloses the 365-day limit and rich data-interpretation rules (UTC to local time, null semantics, fractional minutes), but says nothing about read-only status, error behavior, rate limits, or response shape.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence is well front-loaded, but the definition then runs to several hundred words of consumption/presentation rules. Much of it is domain guidance rather than tool-selection or invocation information, making it excessive for the description field.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description thoroughly covers how to interpret returned health values, which is valuable given there is no output schema. It still omits return structure, pagination, and failure modes, leaving gaps for an agent invoking a query tool with no annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaningful semantics beyond the schema: timezone handling for start/end ISO timestamps, sleep date authority, bodyFat percentage rules, and the distinction between hourly heartRate summaries and daily resting heart rate values.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb (query), resource (health data type), and scope (time-range, last 365 days). It implicitly distinguishes itself from the sibling health_now by being historical/detailed rather than current, though it never names the sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The 'last 365 days' limit is a useful constraint, and 'time-range query' implies historical use versus health_now's present-state use. However, there is no explicit when-to-use/when-not-to-use guidance or named alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
health_nowLatest Apple Health snapshotA
Compact snapshot of the user's most recent sleep, steps, heart rate (latest hourly summary), resting heart rate, walking heart rate average, HRV, workout, weight, body fat, and (if the user opted in separately) menstrual flow samples pushed from their iPhone. Use this for quick, high-frequency status checks. Sleep adds date (wake-up date), totalSleepMinutes, deepMinutes, coreMinutes, remMinutes, awakeMinutes, sleepStart and sleepEnd when available; missing stages are null, and legacy sleep may contain only value/min. Body fat is bodyFat with value as a percentage (23.5 means 23.5%) and unit %.
Health data consumption and presentation (Local and Cloud):
Time/timezone: sleepStart, sleepEnd, start and end may be ISO 8601 UTC timestamps ending in Z. Preserve raw timestamps for storage and calculations. When presenting clock times, convert UTC to the user's local timezone when that timezone is known. Do not display UTC clock times as though they were local. Do not assume the Agent host timezone: a VPS or server may use a different timezone. If the user's timezone is unknown, explicitly label UTC times as UTC or ask for their timezone.
Sleep date: date is already the wake-up calendar date calculated by Mibly using the iPhone's local calendar. Treat date as authoritative for the sleep record. Do not recompute or change date from sleepStart/sleepEnd UTC timestamps.
Sleep minutes: durations and stage values may contain fractional minutes. Preserve raw fractional values for calculations. When presenting whole minutes, round to the nearest minute; do not truncate (for example, 59.6 minutes displays as 60 minutes).
Missing sleep stages: null means stage data is unavailable. Never interpret null as 0. Never invent or infer a missing sleep stage.
Body Fat: bodyFat.value is already a human percentage: 23.5 means 23.5%. Do not divide by 100 or multiply by 100 when presenting it.
Heart rate: a heartRate sample is an hourly summary, not a single reading. value is that hour's average bpm; heartRateSummary has min, avg, max, count and context. context is sedentary (still), active (moving) or unknown (Apple Health did not label it); the same hour can appear once per context. The newest hour may still be in progress and is replaced when updated. Older samples may be single readings without heartRateSummary.
Resting heart rate: restingHeartRate and walkingHeartRateAverage are separate daily values from Apple Health (one per day, bpm). Use restingHeartRate for the user's resting heart rate; never derive it from heartRate minimums or sedentary averages, and do not compare active hours with sedentary hours as if they were the same kind of reading. These are consumption/presentation rules only; preserve the raw Health data and existing calculations.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses that missing stages are null (and must not be read as 0), that heartRate is an hourly summary that can repeat per context, that resting/walking heart rate are separate daily values, and that timestamps are UTC. It stops short of the operational traits an agent might still want (freshness/staleness of the snapshot, whether data can be absent entirely, auth needs).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is front-loaded in one sentence, which is good. But the bulk of the text is output-consumption/presentation policy (rounding rules, timezone conversion, body-fat percentage warnings) that reads like return-value documentation rather than tool selection guidance, and some directives ('preserve raw values') repeat across sections. Useful content, but not tightly scoped to the description's job.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no annotations, the description sensibly compensates by documenting the shape and semantics of most returned fields, especially sleep and heart rate. Steps, HRV, workout, weight, and menstrual flow are only named, not described, leaving some return semantics thin, but overall it is sufficient to call and interpret the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; the 4 baseline applies. The schema is an empty object, so no coverage gap exists.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a precise resource and enumerates its contents (sleep, steps, heart rate, HRV, workout, weight, body fat, menstrual flow), so the agent knows exactly what this returns. It distinguishes itself implicitly as the 'latest/compact' counterpart, but never names the sibling health_detail, so the boundary between the two is only inferable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use this for quick, high-frequency status checks' gives a clear intended context, which is more than nothing. However, it never names health_detail as the alternative, states no when-not-to-use condition, and gives no guidance on choosing between a snapshot and a detailed pull.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v1.2.1- First observed
health_detail - First observed
health_now
TDQS
Scored across 2 tools
health_now returns a compact latest snapshot across multiple health metrics, while health_detail queries a single metric type over a time range. Their purposes are clearly distinct and unlikely to be confused despite sharing a read-only health-data context.
Both tools use snake_case with a shared health_ prefix, which is predictable and consistent. The suffix pattern is not a uniform verb_noun convention (now vs detail), but the set remains readable and coherent.
Two tools is borderline thin for an Apple Health data server. While both roles are justified, there is no discovery, metadata, or multi-type range query tool, making the surface feel minimal.
The surface covers the core retrieval paths: latest snapshot and single-type historical queries up to 365 days. However, it lacks explicit data-type discovery and older-than-365-day access, which are minor gaps agents can work around.
Maintenance
Related MCP Connectors
- SomviaOAuthapp.somvia
Apple Health training load, recovery, HRV and workout detail for Claude, ChatGPT and any MCP client.
MCP server for Withings health data — sleep, activity, heart, and body metrics.
Multi-tenant hosted MCP server for Oura Ring — 21 read-only tools, OAuth per user.
Hosted MCP server for Cliniko — patients, appointments, availability, and invoices for AI agents.
Related MCP Servers
- AlicenseAqualityAmaintenanceLocal-first MCP server that reads Apple Health export files (export.xml/zip) and exposes activity, sleep, HRV, and workout data to AI agents, keeping all data on your machine.18144 npm2MIT
- AlicenseAqualityCmaintenanceApple HealthKit → PostgreSQL sync + MCP server for Claude Code, Cursor, and Ollama. Open-source iOS app with reliable background sync (HKObserverQuery). 11 tools covering health summary, sleep, HRV trends, workouts, daily snapshots, metric stats, period comparison, and coaching briefs. Supports Supabase, Neon, and self-hosted Postgres. Privacy-first: your data goes to your own database.111MIT
- AlicenseAqualityAmaintenanceOpen-source MCP server for MetricBridge (formerly Health Export AI). 16 tools and 22 prompts let any MCP agent query 190 Apple Health metrics from files on your own machine. The server is read-only, has zero dependencies and makes zero network calls.16298 npm7MIT
- AlicenseNot gradedqualityAmaintenanceRead-only MCP server that exposes Apple Health data (steps, workouts, sleep, etc.) from a local SQLite store, allowing AI agents to query health metrics without sending data to hosted services.9Apache 2.0