Metadata-Version: 2.4
Name: soma-mcp
Version: 0.2.0
Summary: MCP server exposing Apple Health, Location, and Activity data from a local Soma SQLite replica
Project-URL: Homepage, https://github.com/dylan-buck/Soma
Project-URL: Repository, https://github.com/dylan-buck/Soma
Project-URL: Issues, https://github.com/dylan-buck/Soma/issues
License: MIT
License-File: LICENSE
Keywords: apple-health,health,healthkit,location,mcp,sensor
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: fastmcp<4,>=3.0
Description-Content-Type: text/markdown

# soma-mcp

MCP server that exposes Apple Health, Location, and Activity data to AI agents.

Reads from a local SQLite replica maintained by the [Soma Mac app](https://github.com/dylan-buck/Soma). Designed for use with any MCP-compatible agent (Claude, ChatGPT, Cursor, etc.).

## Consumer Setup

For end users, the Soma Mac app bundles `soma-mcp` and generates the correct MCP configuration automatically from the menu bar.

## Development Quick Start

```bash
uvx soma-mcp --db ~/.soma/sensors.db
```

## Agent Configuration

### Claude Desktop / Claude Code

For development-only installs, add to your MCP config (`~/.claude/claude_desktop_config.json` or Claude Code settings):

```json
{
  "mcpServers": {
    "soma": {
      "command": "uvx",
      "args": ["soma-mcp", "--db", "~/.soma/sensors.db"]
    }
  }
}
```

### Any MCP Client

The server uses stdio transport. Spawn `soma-mcp` as a subprocess with the `--db` flag pointing to your Soma SQLite replica.

## Tools

| Tool | Description |
|------|-------------|
| `get_user_location()` | Current GPS coordinates, address, and freshness metadata |
| `get_location_history(since?, limit?)` | Location time-series, newest first |
| `get_health_summary()` | Latest sample metrics + current activity + daily snapshots |
| `get_health_metric(metric, since?, limit?)` | Metric history with metric-aware routing |
| `get_health_metrics_list()` | Available metrics inventory with latest values |
| `get_user_activity()` | Current physical activity (stationary, walking, running, etc.) |
| `get_daily_summary(date?)` | Daily health rollup with cumulative and aggregate stats |
| `get_workouts(since?, limit?)` | Workout session history with type, duration, distance, calories |
| `query_sensor_data(sql, limit?)` | Read-only SQL against the local replica |

## Health Metrics (49 across 6 kinds)

**Hourly stats (17):** `steps`, `active_calories`, `basal_calories`, `distance_walking/cycling/swimming`, `workout_minutes`, `flights_climbed`, dietary metrics (energy, water, caffeine, protein, carbs, fat, fiber, sugar, sodium)

**Daily snapshots (2):** `stand_hours`, `sleep_duration`

**Sample events (20):** `heart_rate`, `resting_heart_rate`, `walking_heart_rate_avg`, `blood_oxygen`, `respiratory_rate`, body composition (mass, fat %, BMI, lean mass, height, waist), `hrv`, `vo2_max`, body/wrist temperature, blood pressure, `blood_glucose`, `environmental_audio`, `walking_steadiness`

**Category events (11):** `mindful_session`, cardiac events (low/high HR, irregular rhythm), symptoms (headache, fatigue, nausea), reproductive health (`menstrual_flow`, `cervical_mucus`, `ovulation_test`, `sexual_activity`)

**Activity:** `user_activity` (stationary, walking, running, automotive, cycling, unknown)

**Workouts:** Full `HKWorkout` records with type, duration, distance, energy

## Freshness Metadata

Every response includes freshness fields so agents can assess data recency:

- `recorded_at` — when the sensor reading was taken
- `updated_at` — when it was last applied to local SQLite
- `age_seconds` — how old the reading is
- `stale` — boolean based on metric-specific thresholds

## Requirements

- The [Soma iOS app](https://github.com/dylan-buck/Soma) collecting sensor data on your iPhone
- The [Soma Mac app](https://github.com/dylan-buck/Soma) replicating data to `~/.soma/sensors.db`
- Python 3.10+ (managed automatically by `uvx`)

## Changelog

### 0.2.0

Response field names normalized to snake_case for consistency with SQLite column names and Python conventions:
- `ageSeconds` → `age_seconds`
- `activityLabel` → `activity_label`
- `latestMetrics` → `latest_metrics`

**Backward compatibility:** The old camelCase keys are emitted alongside the new snake_case keys as deprecated aliases. Both forms work in 0.2.x. The camelCase aliases will be removed in 0.3.0.

Added MCP tool annotations (`readOnlyHint`, `idempotentHint`) and server instructions for agent discovery. Error responses for invalid SQL now use MCP `ToolError` instead of raw exceptions.

### 0.1.0

Initial release with all 8 MCP tools.

## Development

```bash
cd soma-mcp
pip install -e .
soma-mcp --db ~/.soma/sensors.db
```
