health-mcp
Allows ingesting health data from Android via Health Connect apps such as HC Webhook or Life Dashboard Companion, including steps, calories, weight, body fat, workouts, and optional sleep.
Allows ingesting health data from iPhone via Health Auto Export, including steps, calories, weight, body fat, workouts, and nutrition.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@health-mcphow many steps did I take yesterday?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
health-mcp
Ask your AI chatbot about your own health data, from a server you own.

health-mcp takes the steps, calories, weight, body fat, workouts and nutrition your phone already collects (iPhone or Android) and makes them available to any AI chatbot that supports MCP connectors. Sleep can be added as an option with the Android apps. Your data stays on your server.
Not medical advice. See Disclaimer.
Quick start · Troubleshooting · Hosted version
How it works
Your phone app sends your data. An export app on your phone (HC Webhook or Life Dashboard Companion on Android, Apple Shortcuts on iPhone, built in with no extra app) posts your health data to your server.
Your server keeps it. health-mcp stores one row per metric per day in a database that you control.
Your chatbot connects. You add your server to your chatbot as an MCP connector. It can read your data, but never change it.
Related MCP server: apple-health-shortcuts-mcp
Quick start
What you need
A computer with a terminal
Node.js 22 or newer
A free Cloudflare account
The phone app: HC Webhook or Life Dashboard Companion (Android), or Apple Shortcuts (iPhone, built in)
A chatbot that supports MCP connectors
About 15 minutes
The terminal is the app where you type commands: Terminal on Mac, PowerShell on Windows. Keep it open for every step below.
1. Deploy your server
The simplest option is Cloudflare Workers with a D1 database. Copy and paste these commands one at a time:
git clone https://github.com/Airbloomer/health-mcp.git
cd health-mcp
npm install
cp wrangler.example.toml wrangler.toml
npx wrangler login
npx wrangler d1 create health-mcpThe last command prints a database_id. Open wrangler.toml in a text editor and paste that value in place of replace-with-d1-database-id. While you are there, change HEALTH_TIMEZONE to your own time zone, for example Europe/Rome.
Optional, Android only: sleep is not stored by default. To keep sleep from HC Webhook or Life Dashboard Companion, add "sleep_analysis" to HEALTH_METRIC_ALLOWLIST in wrangler.toml (it's already there, commented out, in wrangler.example.toml). Remove the # from that line and set it to HEALTH_METRIC_ALLOWLIST = "sleep_analysis", or add sleep_analysis to its comma-separated list, rather than adding a second HEALTH_METRIC_ALLOWLIST line.
Then create the database tables. Do this before you deploy:
node src/cli/d1-migrate.js --remote✅ Checkpoint: the command prints 0001_init.sql through 0006_ingest_backfill_deadline.sql, with no error. --remote or --local is required.
Prefer your own machine, a VPS, or Docker? See Self-hosting for the Node.js and Docker options (SQLite or Postgres).
2. Create your token
The token is the password for your server. Generate a random one:
openssl rand -hex 32Copy the line it prints. That is your <HEALTH_INGEST_TOKEN>. Save it somewhere private, such as a password manager. Then store it on your server and deploy:
npx wrangler secret put HEALTH_INGEST_TOKEN
npx wrangler deployPaste the token when secret put asks for it. wrangler deploy prints your server's address, something like https://health-mcp.<your-subdomain>.workers.dev. Copy it. That address without https:// is your <your-host>.
Anyone with this token can read and write your data. Never share it or post it in a screenshot. More in Security.
✅ Checkpoint: ask your server for a day's summary (on Windows, type curl.exe instead of curl):
curl -H "Authorization: Bearer <HEALTH_INGEST_TOKEN>" "https://<your-host>/summary?date=2026-01-01"You should get back JSON that starts with {"date":"2026-01-01", with empty values for now. {"error":"unauthorized"} means the token doesn't match.
3. Connect your phone
Both apps read Health Connect, so anything Health Connect already collects (for example from a smart scale or a fitness band app) can be sent. Install one of them, grant it Health Connect read access, and add a webhook:
HC Webhook: URL
https://<your-host>/ingest/hc-webhook, format JSON.Life Dashboard Companion: on the Health tab, URL
https://<your-host>/ingest/life-dashboard. Leave Daily totals in payload on.
Then add a header with key Authorization and value Bearer <HEALTH_INGEST_TOKEN> (the word Bearer, a space, then the token). Run a sync (in Life Dashboard Companion, tap Test ping, then Sync Now).
Some setups can't send a header, for example a URL added by QR pairing in Life Dashboard Companion. In that case, create a second, separate token and put it in the URL:
openssl rand -hex 32
npx wrangler secret put HEALTH_INGEST_URL_TOKENThen use the path form: https://<your-host>/ingest/hc-webhook/<HEALTH_INGEST_URL_TOKEN> (or /ingest/life-dashboard/<HEALTH_INGEST_URL_TOKEN>).
Security note: this URL token can only send data, never read it. Still, a URL can end up in logs, history, screenshots and QR codes, and Cloudflare's request logs keep the full URL. Prefer the header when you can. Never put your main
HEALTH_INGEST_TOKENin a URL. If the URL may have leaked, runnpx wrangler secret put HEALTH_INGEST_URL_TOKENagain with a new value. See Security.
Full setup for both apps: Data sources.
Shortcuts is built into iPhone, so there's no extra app to install. A shortcut reads your Health data in daily mode (Find Health Samples with Group By set to Day, one total per metric per day) and sends it to https://<your-host>/ingest/shortcuts with the header key Authorization and value Bearer <HEALTH_INGEST_TOKEN> (the word Bearer, a space, then the token).
Full setup: Shortcuts guide
Note: these steps are tested against the server but not yet verified on a device.
Prefer a dedicated app? See Data sources.
✅ Checkpoint: after your first sync (on iPhone, run the shortcut once), run the same curl command from step 2 with yesterday's date (YYYY-MM-DD). You should now see numbers, such as "steps", instead of empty values.
4. Connect your chatbot
Every chatbot that supports remote MCP servers needs the same two things:
Server URL:
https://<your-host>/mcpAuthorization header:
Bearer <HEALTH_INGEST_TOKEN>
Your server must be reachable on the public internet (the workers.dev address is).
Go to Customize > Connectors, click Add, then add a custom connector. Enter a name and the server URL, choose No sign in, and under Request headers add Authorization with the value Bearer <HEALTH_INGEST_TOKEN>. Request headers are a beta feature; see Claude's help article. On Team and Enterprise plans, an owner adds the connector first.
Not supported yet: ChatGPT custom connectors need OAuth; health-mcp uses a bearer token. OpenAI's custom MCP server documentation lists OAuth or no authentication as the only options.
Add a remote MCP server with the URL and header above. A Cursor mcp.json example is in the API reference.
✅ Checkpoint: the connector shows four tools: get_daily_summary, get_metric, list_metrics, and list_workouts.
5. Try it
Start a new chat with the connector turned on and ask:
"How did I train this week?"
"How many steps did I take yesterday?"
"How did I sleep this week?" (Android, with sleep enabled)
"Show my weight over the last 30 days."
Your data appears only after the phone app's first sync. The sleep question works only with Android, after you enable sleep with the optional HEALTH_METRIC_ALLOWLIST setting in step 1.
✅ Checkpoint: the chatbot calls a health-mcp tool and answers with your numbers.
Recommended export settings
Health Auto Export should post recent days, not a full history and not today alone. Create two REST API automations (the app allows one data type each) that both post to https://<your-host>/ingest with the header Authorization: Bearer <HEALTH_INGEST_TOKEN>.
Format: JSON, export version 2.
Metrics: Dietary Energy, Protein, Carbohydrates, Total Fat, Active Energy, Basal Energy Burned, Step Count, Weight, and Body Fat Percentage. Use one total per metric per day, not hourly buckets.
Date range: today and yesterday. Yesterday is the finished day. Today is the running total, sent again as the day fills in. A range of today only never sends yesterday's finished total. After the initial import window closes, points older than yesterday are dropped (
INGEST_MAX_AGE_DAYS, default1, counted in the configured time zone). A date more than one day ahead is dropped too. Dates beforeINGEST_MIN_DATE(default2000-01-01) are dropped as well.Schedule: a few times a day, not every hour. The two automations share one token, and each run is a post. The default limit is 6 accepted posts per token per hour (
INGEST_RATE_LIMIT_PER_HOUR).Workouts: a second automation with the same URL, header, format, version, and date range. Leave route and per-second workout metrics off.
Batch requests: leave them off, so one day is not split across posts.
A one-time full history can be posted here during the initial import window, or at any time on POST /import/apple-health. Import that history in chunks of 5 MB or less. An Apple Health import is stored in batches of at most 366 dates, and it is not rejected because of the date count. That import does not open, close, or seal the initial import window, it does not draw down the window byte total, and it does not apply the recent-day filter. It still drops dates before INGEST_MIN_DATE. On D1, a request that would run more than 45 statements returns 413 and names node src/cli/import-apple-health.js --chunk-url. SQLite and Postgres do not use that statement budget. Setup details are in Data sources.
Ingest limits
The first accepted ingest opens an initial import window. There is no separate setup step. Keeping past dates is limited to Health Auto Export JSON on POST /ingest. The raised JSON body cap and the higher hourly cap apply to JSON ingest routes while the window is open. Each of those bodies may be up to INGEST_BACKFILL_MAX_BYTES (default 5,000,000, and never above 10,000,000), and each token may send INGEST_BACKFILL_MAX_PER_HOUR posts (default 60). Import history in chunks of that size or less. The hint text uses the configured cap, and the default wording is 5 MB. The window still rejects a date more than one day ahead, and a date before INGEST_MIN_DATE (default 2000-01-01). A configured minimum before 1970-01-01 is raised to that day, a configured minimum after today is lowered to today in UTC, and a value that is not a real calendar day uses the default. A JSON ingest request may cover at most 366 distinct dates. A total of INGEST_BACKFILL_MAX_TOTAL_BYTES (default 500,000,000) applies to non-Apple JSON bodies while the window is open. An Apple Health import does not draw that total down. Unchanged days are still skipped.
A request whose content-type does not contain json stays on the steady size cap. An Apple Health export on POST /ingest uses the 20,000,000 byte cap, not the import-window per-request cap.
The window closes for good at the first of these: the deadline stored when it opened (INGEST_BACKFILL_HOURS, default 72), the total stored when it opened, or a request with "backfill": "done" or the header X-Backfill: done. Changing those settings later does not extend a window that is already open, and it does not reopen a closed one. A database that already holds points, workouts, or raw payloads receives a closed row when its schema is updated, so those rows do not start a window. A database that already has non-Apple data and no window row is treated as closed. An Apple Health import does not open, close, or seal the window, and it does not draw down the window byte total. An Apple import before the first accepted ingest still lets that ingest open the window. On D1, one request may run at most 45 statements. INGEST_MAX_STATEMENTS can only lower that ceiling. A Health Auto Export request over the budget returns 413 and names POST /import/apple-health/chunk. SQLite and Postgres do not apply this budget. A missing row is not an open window, and a response before that row exists reports it closed. A failed write does not reopen a closed window. Postgres keeps each request's reservation and its writes on one connection, so a failure rolls all of them back. SQLite and D1 keep the bytes for each group that was stored. After the window is closed, bytes_remaining is 0 and the steady limits apply again: today and yesterday, INGEST_MAX_BODY_BYTES (default 1,000,000), and INGEST_RATE_LIMIT_PER_HOUR (default 6). Every ingest response includes backfill with open, closes_at, and bytes_remaining. A chunk that is too large during the window returns 413 and asks for a shorter date range. After the window is closed, an old date returns 422 and an oversized or too-frequent post returns 413 or 429, and the message says the initial import window is closed.
MemoryStorage keeps this window in the process only. It is not persistent. A new process has no row and no stored data, so the next stored point can open a window. A restart does not keep a closed window.
Troubleshooting
The chatbot or phone app says unauthorized (
401). Check that the header is exactlyBearer, one space, then the token. The URL token works only on threePOSTroutes: the two Android webhook routes (/ingest/hc-webhook,/ingest/life-dashboard) and/ingest/shortcuts. It never works for/mcpor/summary.Sending data fails right after deploying. Run
node src/cli/d1-migrate.js --remote(or--localfor a local database), thennpx wrangler deployagain.A day is missing or shows up on the wrong date. Set
HEALTH_TIMEZONEinwrangler.tomlto your zone, then runnpx wrangler deployagain.Totals look too low. Send whole days. On the app routes that compare sample counts, a later sync with fewer samples for a day does not replace a fuller one. A Shortcuts daily total sent with the Bearer token replaces the day. See Data sources.
Nothing arrives from the iPhone. Run the shortcut manually with the phone unlocked, and check the
Authorizationheader.export_hintis set on a summary. A recent post looked like too much history, every date in a post was too old, or posts arrived too often. Follow Recommended export settings. A later accepted post clears it.
More documentation
API reference: routes, payload formats, limits, and MCP tools
Data sources: every supported exporter and full app setup
Self-hosting: Cloudflare, Docker, Node, configuration, migrations, development
Security: tokens, rotation, URL-token caveats, logs, and browser clients
Disclaimer
health-mcp is an independent open-source project by Airbloomer, LLC. It is not affiliated with, endorsed by, or sponsored by Apple Inc. or Google LLC. Apple, iPhone, Apple Watch, Apple Health, HealthKit and Shortcuts are trademarks of Apple Inc., registered in the U.S. and other countries. Google, Android and Health Connect are trademarks of Google LLC. They are used here only to describe compatibility.
health-mcp is not a medical device and does not provide medical advice, diagnosis or treatment. Talk to a qualified professional about any health decision.
health-mcp is self-hosted software. You run it and you control your data, and you are responsible for securing your server. Airbloomer, LLC does not host, receive or access your health data.
Any third-party apps mentioned in this project's docs are owned by their respective owners and are not affiliated with or endorsed by Airbloomer, LLC. Use them under their own terms and privacy policies.
Provided under the license in LICENSE, "as is", without warranty of any kind.
Hosted version
We're exploring a hosted health-mcp for people who don't want to run their own server. It isn't available, and there's no commitment to launch it, or to any pricing or timing. To hear if it happens, email oss@airbloomer.com with the subject 'hosted waitlist'. Please don't include any health information in your email. We'll use your email address only to contact you about a hosted version, keep it no longer than 24 months, and delete it sooner if you ask (reply 'remove' at any time). Details: https://airbloomer.com/privacy. Nothing in this section changes the self-hosted software: Airbloomer, LLC still does not host, receive or access any of your health data.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Your Apple Health data, food diary and lifts, for ChatGPT, Claude and Gemini. Approved on iPhone.
Read-only Apple Health data synced from your iPhone: sleep, heart rate, HRV, workouts and trends.
- SomviaOAuthapp.somvia
Apple Health training load, recovery, HRV and workout detail for Claude, ChatGPT and any MCP client.
Connect your health, fitness, nutrition, sleep, and wearable data to your AI assistant.
Related MCP Servers
- AlicenseAqualityAmaintenanceRead-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.99Apache 2.0
- AlicenseNot gradedqualityCmaintenanceMCP server that provides read-only access to Apple Health data via three tools (health_now, health_detail, health_trends), letting AI assistants query recent activity, sleep, and trends securely through a Cloudflare-deployed API.2MIT
- AlicenseNot gradedqualityCmaintenanceExposes Apple Health data as three read-only MCP tools (current status, details, trends) via a Cloudflare Worker, enabling AI assistants to view health metrics without modification.MIT
- AlicenseNot gradedqualityCmaintenanceExposes Apple Health data as three read-only MCP tools (now, detail, trends) for AI to query health metrics, sleep, and trends via HTTP.MIT