Skip to main content
Glama

English · 繁體中文 · 日本語

Health Data Hub: iPhone collector + local Mac database + MCP

A lightweight, personal health-data collector. It moves the data in your iPhone's Health app to your own Mac, stores it as one SQLite file, and lets AI assistants such as Claude and Codex query it. No cloud, no account, no server.

Version 0.3.2 (changelog) · Status: in daily use by the author since 2026-10 (as of 2026-10-06) · iPhone with iOS 18+ and a Mac on the same Wi-Fi.

Want an AI assistant to install it for you? Hand docs/AGENT_INSTALL.md to Claude Code or Codex and it will follow the steps.

What it is

Three small parts in a line:

  1. iPhone collector app (native Swift): reads the Health app and pushes new data to the Mac.

  2. Mac receiver (Python, FastAPI): receives the data on your home Wi-Fi and writes it to the local SQLite file data/health.sqlite.

  3. MCP server (Python, no dependencies): lets Claude Code, Codex and other AI assistants query the data, build weekly reports, and log workouts and meals by hand.

What it does

  • Collects 11 data types automatically: body mass, lean body mass, body fat %, BMI, waist circumference, steps, heart rate, resting heart rate, heart rate variability (HRV), sleep and workouts. Field meanings: docs/health-data-fields.md.

  • Anything a smart scale or watch writes into the Health app is collected too — the collector reads the Health app and does not care which device wrote the data.

  • Ask your AI assistant directly: "How did my weight change last week?", "Am I sleeping enough?", "Give me this week's report."

  • Log workouts and meals by hand (log_workout / log_meal).

Why

The Health app keeps its data on the phone: exporting means tapping through menus, the export is one huge XML file, and AI assistants cannot see it. Most sync services upload the data to someone else's server.

This project takes another route: the data only travels between your own phone and your own Mac, lands in a SQLite file you can open directly, and reaches your local AI assistant through MCP.

Scope

Built for one person, one iPhone and one Mac. It is not a multi-user platform.

Advantages:

  • Private: phone → home Wi-Fi → your Mac. No third party in between.

  • No loss, no duplicates: the phone keeps a sync position per data type; batches that fail to send wait in a local queue on the phone and are resent unchanged; the Mac deduplicates by sample uuid, so a resend is stored once.

  • Open format: one SQLite file any tool can read; schema in server/schema.sql.

  • Ready for AI: the MCP server plugs into Claude Code and Codex as is.

  • Light: no cloud service, database server or Docker. The Mac runs one Python process.

  • Tested: automated tests on both the Python and Swift sides, plus on-device acceptance.

Limits — what it does not do:

  • iPhone + Mac only. No Android, Windows or Linux phone side. The receiver can run anywhere Python runs, but server/run.sh uses macOS networksetup and dns-sd.

  • Read-only. It never writes back to the Health app.

  • iOS background wake-ups are unreliable. iOS decides when to wake the app; the author measured 2 background wake-ups in 33 hours. So a Shortcuts automation opens the app on a schedule, which guarantees at least one sync a day, not real time.

  • Phone and Mac must share a Wi-Fi network (or you set up Tailscale as a backup path). While the Mac is off or asleep the data waits in the phone's queue and is resent later.

  • Scale data must reach the Health app first. A Eufy scale, for example, writes through its own app; only then can the collector read it.

  • Only the 11 types above. No ECG, blood oxygen, medications, etc.

  • No web UI. You query through MCP (an AI assistant) or read SQLite directly.

  • The app is not on the App Store. You build it with Xcode and install it yourself. A free Apple ID works, but the installed app expires after 7 days and must be reinstalled.

How it works

iPhone
  ┌─ Health app (HealthKit)
  │      │ read only
  │      ▼
  └─ Collector app
       · one sync position per data type
       · failed batches are queued and resent
       · syncs when the app opens / on background wake
             │
             │  HTTP POST + Bearer token  →  healthhub.local:8765
             ▼
Mac (same Wi-Fi)
  ┌─ Receiver: server/run.sh (FastAPI :8765)
  │      POST /ingest · GET /health
  │      │ dedupe by uuid, write
  │      ▼
  ├─ data/health.sqlite
  │      │
  │      ▼
  └─ MCP server health-hub  ←  Claude / Codex
  • Incremental sync: the app uses HealthKit anchored queries to fetch only what is new. A type's anchor moves forward only after the Mac confirms receipt, so a failure halfway never loses data.

  • When it syncs: when the app opens (with the "打开 App 时自动同步" — auto-sync on open — switch on), and when iOS wakes it in the background, which is rare. A daily Shortcuts automation is the safety net.

  • Finding the Mac: the receiver binds to the Mac's Wi-Fi address and announces the Bonjour name healthhub.local, so the phone keeps working when the Mac's IP changes.

  • Auth: the Mac generates a 32-character access token; it is baked into the app at build time (locally, never in git) and sent with every request.

  • Full protocol: docs/sync-protocol.md.

Install

Prerequisites

  • A Mac with Xcode (iOS 18 SDK or later), uv, XcodeGen (brew install xcodegen) and Python 3.9+.

  • An iPhone on iOS 18+, connected by cable, with Developer Mode on.

  • An Apple ID signed in under Xcode → Settings → Accounts (a paid developer account is easier).

Step 1: Mac receiver

git clone <this-repo-url> health-hub && cd health-hub
uv sync                                   # install Python dependencies
uv run python -m server.token new         # create the access token in data/ingest_token (mode 0600)
server/run.sh                             # start the receiver: Wi-Fi address :8765, announces healthhub.local

Check from another terminal: curl http://healthhub.local:8765/health returns {"status":"ok"}. On first run macOS may ask whether to accept incoming connections — choose Allow. To keep it running, start server/run.sh from launchd or your login items.

Step 2: build and install the iPhone app

  1. Create ios/local.xcconfig (already in .gitignore):

    DEVELOPMENT_TEAM = <your 10-character Team ID>
    PRODUCT_BUNDLE_IDENTIFIER = com.<your-name>.healthcollector

    security find-identity -v -p codesigning shows the Team ID in parentheses.

  2. Bake the Mac address and token into the app, generate the project and build:

    ios/make_local_config.sh                 # writes ios/HealthCollector/LocalConfig.plist (not in git; never prints the token)
    cd ios && xcodegen generate
    xcodebuild -project HealthCollector.xcodeproj -scheme HealthCollector \
      -destination 'generic/platform=iOS' -configuration Debug \
      -allowProvisioningUpdates build

    Or, after xcodegen generate, open ios/HealthCollector.xcodeproj in Xcode, pick your iPhone and press Run.

  3. Install on the phone from the command line:

    APP=$(find ~/Library/Developer/Xcode/DerivedData -path '*Debug-iphoneos/HealthCollector.app' | head -1)
    xcrun devicectl list devices                                   # find your device name
    xcrun devicectl device install app --device "<device-name>" "$APP"

    On first install, trust the developer on the phone under Settings → General → VPN & Device Management.

Step 3: first run on the phone

The app's interface is in Chinese; the English meaning is in parentheses.

  1. Open the collector, tap 授权读取健康数据 (allow reading health data) and allow everything in the system sheet.

  2. Tap 测试连接 (test connection); it should succeed — the Mac address and token are already filled in.

  3. Tap 立即同步 (sync now). The first sync uploads your full history to the Mac.

  4. Turn on 打开 App 时自动同步 (auto-sync on open). The same switch also controls background sync.

iOS rarely wakes the app in the background, so in the Shortcuts app → Automation, create two automations:

  • Time of Day: every day at 12:00 → action "Open App" → the collector → "Run Immediately".

  • Charger: when connected → open the collector → "Run Immediately".

Opening the app triggers a sync. Automations can only be created on the phone, not from the Mac.

Usage

Connect an AI assistant (MCP)

Claude Code: .mcp.json at the repo root already registers health-hub. Start claude in the repo; the first time it asks whether to enable this project MCP server — approve it.

Codex: add to ~/.codex/config.toml (use your absolute path):

[mcp_servers.health-hub]
command = "python3"
args = ["/absolute/path/health-hub/src/health_hub_mcp/server.py"]

Then ask: "health-hub weight for the last 7 days", "weekly report", "how is sync doing?"

Tool

What it does

query_samples

query samples by type and time range

weekly_report

one-week summary (weight, steps, sleep, heart rate…)

sync_status

last sync time and row counts per type

log_workout / log_meal

log a workout / meal by hand

ios_env_check / ios_build / ios_install / ios_logs

for developing the app: check the environment, build, install, read logs (output redacted)

kb_list / kb_search

search the bundled Apple HealthKit documentation

Without an AI assistant, from the command line:

python3 src/health_hub_mcp/server.py sync-status     # sync status
python3 src/health_hub_mcp/server.py env-check       # iOS development environment check
python3 src/health_hub_mcp/server.py kb-search "background delivery"

Variable

Purpose

Default

HEALTH_HUB_DATA_DIR

data directory

data/ in the repo

HEALTH_HUB_INGEST_TOKEN

access token (wins over data/ingest_token)

—

HEALTH_HUB_BUNDLE_ID

bundle ID used by ios_install / ios_logs

PRODUCT_BUNDLE_IDENTIFIER from ios/local.xcconfig

HEALTH_HUB_DEVICE

device name used by ios_install / ios_logs

— (put it in the git-ignored local.env)

Agent skill: ios-dev-env

The repo ships a Claude Code skill at .claude/skills/ios-dev-env/SKILL.md. It teaches the assistant to check the iOS toolchain, generate the project, build, install, read logs and debug signing, and it keeps secrets (Team ID, certificates, device IDs) out of chat and git.

  • In this project only: nothing to install. Claude Code loads project skills from .claude/skills/ when started in the repo. Say "build the collector", "install on my phone", "signing error", or type /ios-dev-env.

  • In every project: copy it into your personal skills directory:

    mkdir -p ~/.claude/skills && cp -R .claude/skills/ios-dev-env ~/.claude/skills/
  • Codex: ask Codex to read .claude/skills/ios-dev-env/SKILL.md and follow it; if your Codex version supports skills, you can copy the folder into its skills directory (see the Codex docs).

  • Before first use: keep your own values out of git — device name in local.env, bundle ID in ios/local.xcconfig, and anything else about your machine in a git-ignored .claude/skills/ios-dev-env/local.md, which the skill reads first.

Documentation

Document

What is in it

Health data fields

What the Health app can give you: the 11 types collected today with meaning and unit, every database column, sleep states, common metadata keys, and HealthKit types that could be added later

Sync protocol

How the phone talks to the Mac: request and response fields, examples, timestamps, deduplication, transport, auth

Agent install guide

Step-by-step install for Claude Code / Codex, with a check after every step

Project structure

Every file and folder, and where new things go

Changelog

What changed in each version

Project layout

Path

Purpose

ios/CollectorCore/

Swift package: sync engine, queue, encoding — all testable logic

ios/HealthCollector/

thin iPhone app shell (HealthKit access, UI, background delivery)

server/

Mac receiver: FastAPI app, token tool, run.sh, SQLite schema

src/health_hub_mcp/

MCP server, also usable from the command line

kb/

bundled Apple documentation for the MCP knowledge-base tools

docs/

protocol, field reference, plans, device test records, agent install guide

tests/

pytest suites and fixtures

data/

your database and token — git-ignored, never leaves the Mac

Every file and folder: STRUCTURE.md.

For developers

uv run pytest                                          # Python: unit / integration / contract / e2e
swift test --package-path ios/CollectorCore            # Swift collector core

Changes per version: CHANGELOG.md · How to contribute: CONTRIBUTING.md · Reporting security problems: SECURITY.md.

Privacy

  • data/ (database, token), ios/local.xcconfig, ios/HealthCollector/LocalConfig.plist and the generated .xcodeproj are all git-ignored.

  • tests/_privacy.py checks that identifiers such as Team IDs, device UDIDs, Tailscale IPs and certificate fingerprints never enter the repo.

License

MIT. The optional reference material described in kb/README.md is not included and keeps its own terms.

Related MCP Connectors