Skip to main content
Glama
README.md
# iwatch-mcp

Real-time Apple Watch heart rate monitoring via the [Model Context Protocol](https://modelcontextprotocol.io).

Heart rate data flows: **Apple Watch → iPhone → Mac (via iCloud/HealthKit)**.  
This server reads it from macOS HealthKit and exposes it as MCP tools to Claude or any MCP client.

## Architecture

```
Claude / MCP client
      │  MCP (stdio)
      ▼
iwatch-mcp  (Python)          ← pip install
      │  subprocess
      ▼
HealthKitHelper  (Swift CLI)  ← ./healthkit-helper/build.sh
      │  HealthKit API
      ▼
macOS HealthKit database      ← synced from Apple Watch via iPhone
```

## Prerequisites

| Requirement | Notes |
|---|---|
| macOS 13+ | HealthKit for Mac requires Ventura or later |
| Xcode | Installed from the App Store (needed to build Swift helper) |
| Apple Developer account | Required to sign the binary with HealthKit entitlement |
| Apple Watch paired | Data syncs through iPhone → iCloud → Mac |
| Python 3.11+ | For the MCP server |

> **Why signing is required:** `com.apple.developer.healthkit` is a restricted entitlement.  
> macOS will reject HealthKit calls from binaries not signed with a valid Apple Developer certificate.

## Setup

### 1 — Build and sign the Swift helper

```bash
cd healthkit-helper
./build.sh
```

`build.sh` automatically finds the best signing identity in your keychain  
(prefers "Developer ID Application", falls back to "Apple Development").

To list available signing identities:
```bash
security find-identity -v -p codesigning
```

To sign manually with a specific identity:
```bash
codesign --force \
         --sign "Apple Development: you@example.com (TEAMID)" \
         --entitlements HealthKitHelper.entitlements \
         --options runtime \
         .build/release/HealthKitHelper
```

### 2 — Grant HealthKit access

Run the helper once to trigger the macOS permission dialog:
```bash
./healthkit-helper/.build/release/HealthKitHelper latest
```

When prompted, open **System Settings → Privacy & Security → Health** and enable  
**HealthKitHelper** for reading heart rate data.

### 3 — Install the Python MCP server

```bash
pip install -e .
```

Or with pipx (recommended for isolation):
```bash
pipx install .
```

### 4 — Connect to Claude

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`.

**Option A — venv entrypoint (recommended):**
```json
{
  "mcpServers": {
    "iwatch": {
      "command": "/Users/ehuang/Repos/iwatch-mcp/.venv/bin/iwatch-mcp"
    }
  }
}
```

**Option B — run via python module:**
```json
{
  "mcpServers": {
    "iwatch": {
      "command": "/Users/ehuang/Repos/iwatch-mcp/.venv/bin/python",
      "args": ["-m", "iwatch_mcp.server"]
    }
  }
}
```

Restart Claude Desktop. The `iwatch` server will appear in the MCP panel.

## MCP Tools

### `get_heart_rate`
Returns the most recent heart rate sample from HealthKit.

**Example response:**
```json
{
  "bpm": 72.0,
  "timestamp": "2025-06-15T14:32:00.000Z",
  "source": "Apple Watch",
  "device": "Apple Watch Series 9"
}
```

---

### `stream_heart_rate(duration_seconds: int = 30)`
Collects readings over a rolling time window.  
Immediately returns any buffered readings from the last 5 minutes, then  
watches for new samples for `duration_seconds` seconds.

**Note:** Apple Watch syncs over iCloud — "real-time" latency is typically 10–60 seconds.

**Example response:**
```json
[
  {"bpm": 68.0, "timestamp": "2025-06-15T14:31:45.000Z", "source": "Apple Watch", "device": "Apple Watch Series 9"},
  {"bpm": 70.0, "timestamp": "2025-06-15T14:32:00.000Z", "source": "Apple Watch", "device": "Apple Watch Series 9"}
]
```

---

### `get_heart_rate_history(hours: float = 24.0)`
Retrieves and summarises historical heart rate data.

**Example response:**
```json
{
  "count": 42,
  "hours_requested": 24.0,
  "min_bpm": 52.0,
  "max_bpm": 143.0,
  "avg_bpm": 71.3,
  "first_timestamp": "2025-06-14T14:35:00.000Z",
  "last_timestamp": "2025-06-15T14:32:00.000Z",
  "samples": [...]
}
```

## MCP Resource

`healthkit://heart-rate/status` — reports whether the helper binary is built and ready.

## Environment Variables

| Variable | Default | Description |
|---|---|---|
| `HEALTHKIT_HELPER_PATH` | `healthkit-helper/.build/release/HealthKitHelper` | Override binary location |

## Troubleshooting

### "HealthKit is not available on this device"
HealthKit requires macOS 13+ on Apple Silicon (M1/M2/M3 Mac). It is not available on Intel Macs.

### "Auth failed" / empty results after first run
1. Open **System Settings → Privacy & Security → Health**
2. Scroll to find **HealthKitHelper** and toggle on **Heart Rate**
3. Also ensure the Health app is open and signed into iCloud

### No data / stale data
Health data syncs from Apple Watch through iPhone. Make sure:
- iPhone is nearby and connected (Wi-Fi or Bluetooth to Mac)
- iCloud Drive is enabled on both iPhone and Mac
- Health app is signed into the same Apple ID on Mac

### Binary not signed / HealthKit permission denied
HealthKit requires a real Apple Developer certificate. Ad-hoc signing (`-s -`) does not work  
for the `com.apple.developer.healthkit` entitlement. You need either:
- A paid Apple Developer Program membership ($99/year)
- Or a free Apple Developer account (limited entitlements — HealthKit is included for device testing)

### Test the helper directly
```bash
# Latest reading
./healthkit-helper/.build/release/HealthKitHelper latest

# 24 hours of history
./healthkit-helper/.build/release/HealthKitHelper history 24

# Stream for 60 seconds
./healthkit-helper/.build/release/HealthKitHelper stream 60
```

TDQS

A4.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool serves a distinct purpose: one returns the most recent single reading, one retrieves historical data over a time window, and one streams live data. No overlap.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern using snake_case: get_heart_rate, get_heart_rate_history, stream_heart_rate.

Tool Count5/5

Three tools is appropriate for heart rate access: current reading, history, and streaming. Not too few nor too many.

Completeness4/5

CRUD-like coverage: read single, read history, stream. Minor gap: no way to query a specific time range outside 'past N hours' or duration.

Maintenance

ActivityInactive
ResponsivenessNo issues