Skip to main content
Glama
README.md
# fitbit-air-coach

Sync your Fitbit Air data from the Google Health API to a local SQLite database on your Mac, analyse it with cited
science, and talk to it through Claude as a personal coach.

> Status: experimental (v0.1.0). Unofficial — not affiliated with or endorsed by Fitbit, Google or Anthropic.
> Not medical advice. See [DISCLAIMER.md](DISCLAIMER.md).

The repository is fitbit-air-coach; the package and command are `fitbit-air`.

## What it does

fitbit-air has four parts. They share one private home folder, `~/.fitbit-air`.

- **Sync:** reads your data from the Google Health API v4 (`health.googleapis.com/v4`) with your own OAuth client,
  into a local SQLite database. A launchd job syncs every 2 hours, at login, and after wake if a run was missed.
- **Analysis:** personal baselines, sleep and recovery scores, strain signals, safety rails, activity, weight,
  running and a race plan, and body age. Every number cites a study or a stated design choice.
- **Coach:** a local MCP server for Claude Desktop and Claude Code. Claude reads your scores and trends, runs
  read-only SQL, and keeps a journal and coach notes on your Mac.
- **Dashboard:** a page you publish as a private Claude Artifact. It shows live numbers in Claude Desktop on your
  Mac, and a daily snapshot anywhere else.

## Features

- **27 Google Health data types:** heart rate, HRV, resting HR, sleep stages, SpO2, respiratory rate,
  temperature during sleep, steps, active zone minutes, exercise sessions, VO2 max, weight, body fat and more.
- **A sync you can leave running:** a resumable backfill back to 2015, a 3-day overlap on every sync, a daily 14-day
  re-scan for late data, throttling and retries. A file lock stops two syncs running at once.
- **A raw layer:** every API payload is kept verbatim in `raw_points`. `fitbit-air rebuild` regenerates the clean
  tables from it without calling Google.
- **Analysis on your own baselines:**
  - sleep score on duration, regularity (SRI), efficiency and timing;
  - recovery score from HRV, resting HR and sleep;
  - strain signals (an illness early warning) and an experimental stress index;
  - absolute safety rails, shown before everything else;
  - activity (steps, moderate-to-vigorous minutes, sedentary time, strength), weight trend and BMI;
  - running: heart-rate zones, a single-run spike rule, a race plan and a time estimate;
  - body age, with its range and caveats.
- **Honest confidence:** scores say "building baseline" until there is enough data, and recovery findings need a
  minimum baseline confidence.
- **Cited science:** each score cites sources in `src/fitbit_air/analysis/evidence.toml`. See
  [docs/science.md](docs/science.md).
- **The coach:** read tools (overview, trends, sleep, activity, running, body age, evidence, data status), one
  read-only SQL query at a time, a local journal and coach notes, profile edits, a "refresh now" tool, and four
  prompts: `morning_checkin`, `weekly_review`, `plan_next_run` and `explain_score`.
- **Customization:** starter templates, your profile, threshold overrides within bounds, the coach's persona and
  prompts, and the dashboard layout. `fitbit-air config check` validates everything.
- **Plugins:** small Python files that add your own daily metrics, findings and read-only coach tools. Off by
  default.

## Compatibility and prerequisites

- **macOS:** tested. The login token lives in the macOS Keychain and the background sync uses launchd.
- **Linux:** not tested yet. CI is set up to run the tests on Ubuntu, but Linux is not supported.
- **Python 3.12** and [uv](https://docs.astral.sh/uv/). uv installs Python for you if needed.
- **A Fitbit Air**, set up in the Google Health app.
- **A Google Cloud OAuth client** with the Google Health API enabled. You create it yourself in a free Google Cloud
  project; [docs/setup.md](docs/setup.md) walks through it.
- **Claude Desktop or Claude Code** for the coach and the dashboard. Sync, analysis and `report` work without them.

## Quick start

```bash
git clone https://github.com/v4ikartana/fitbit-air-coach
cd fitbit-air-coach
uv sync
uv run fitbit-air init --template minimal      # or runner-10k, sleep-recovery
# Create your Google OAuth client (docs/setup.md), then save its JSON file as:
#   ~/.fitbit-air/secrets/client_secret.json
uv run fitbit-air auth                         # log in with Google in your browser
uv run fitbit-air sync                         # fetches new data and backfills any unfinished history
uv run fitbit-air schedule install             # background sync via launchd (see below)
uv run fitbit-air report                       # today's safety checks, scores and findings
```

The background job syncs every 2 hours, at login, and after wake if a run was missed. While your OAuth app is in
Testing mode, Google expires the login every 7 days; run `fitbit-air auth` again when you are asked to. The full
walkthrough, including the coach registration and the dashboard, is [docs/setup.md](docs/setup.md).

## Configuration

Everything personal lives in `~/.fitbit-air` (or the folder in `FITBIT_AIR_HOME`), never in the repo.

- **Templates:** `init --template` starts you with `minimal`, `runner-10k` or `sleep-recovery`.
- **Profile** (`config/profile.toml`): sex, sleep need, goals, race date and distance, height, units.
- **Thresholds** (`config/thresholds.toml`): override any analysis parameter, with a reason. Values stay within
  bounds, and safety rails can only get stricter.
- **Coach** (`config/coach/`): your persona and your own prompts. The core safety rules always apply.
- **Dashboard** (`config/dashboard.toml`): tabs, cards, trend metrics, default range and colours.
- **Plugins** (`plugins/*.py`): your own metrics, findings and coach tools.

A broken file never breaks a sync: the analysis falls back to the built-in defaults for that file and reports the
problem. Details: [docs/configuration.md](docs/configuration.md) and [docs/plugins.md](docs/plugins.md).

## Usage

```bash
uv run fitbit-air status               # login state, what's synced, pending rebuilds, row counts
uv run fitbit-air sync [--backfill]    # fetch new data; --backfill re-walks the full history
uv run fitbit-air report [--date D]    # a day's safety checks, scores and findings
uv run fitbit-air analyze [--since D]  # recompute baselines, scores and findings (default: last 60 days)
uv run fitbit-air rebuild [--since D]  # regenerate clean tables from the raw layer
uv run fitbit-air profile show|set K V # your analysis profile
uv run fitbit-air config show|check    # effective settings and where they come from; validate them
uv run fitbit-air dashboard build      # the dashboard page with your layout
uv run fitbit-air snapshot             # the dashboard's daily snapshot
uv run fitbit-air schedule install|uninstall
```

**The coach:** register `fitbit-air mcp` once with Claude Desktop (`scripts/register_claude_desktop.py`) or Claude
Code (`claude mcp add-json`), as described in [docs/setup.md](docs/setup.md#9-register-the-coach-with-claude).
Claude then starts the server itself. Pick a prompt such as `morning_checkin`, or just ask for a weekly review or
the plan for your next run.

## Architecture

The sync writes every Google payload to a raw table, then normalises it into clean tables (heart rate, sleep,
activity, daily metrics). After each sync, the analysis recomputes the last 60 days into analysis tables (daily
metrics, scores, findings, runs), using parameters from `evidence.toml` and your config. The CLI report, the MCP
coach and the dashboard snapshot all read those tables through the same functions. See
[docs/architecture.md](docs/architecture.md).

## Security and privacy

- **Your data stays local:** the database, config and logs live in `~/.fitbit-air`. The folder is 0700 and the
  files fitbit-air writes are 0600.
- **Your login:** the Google refresh token is stored in the macOS Keychain. Access tokens are kept in memory only.
  The OAuth scopes are read-only, and nothing is ever written to Google.
- **What leaves your Mac:**
  - requests to Google: the OAuth login and the Google Health API calls;
  - whatever Claude reads into a conversation through the coach;
  - the dashboard snapshot (under 240 KB), if you publish the dashboard: your data status and overview, 30- and
    90-day trends, the last 14 days of sleep, 30 days of activity and 90 days of runs, body age and your active
    coach notes, stored privately in your Claude Artifact;
  - the dashboard page loads its fonts from Google Fonts (`fonts.googleapis.com`) whenever it is viewed.
- **No telemetry:** apart from the above, the code sends nothing anywhere. There is no analytics or usage
  reporting.
- **Plugins** run with your user's permissions. Only install code you wrote or have read.

To report a vulnerability, see [SECURITY.md](SECURITY.md).

## Known limitations

- **Fitbit Air and the Google Health API v4 only.** Other devices and APIs are not supported.
- **macOS only:** the background sync uses launchd and the token uses the Keychain.
- **Testing mode:** while your OAuth app is in Testing, Google expires the login every 7 days.
- **Body age** uses reference data for men, so it is only calculated when the profile says `sex = "male"`.
- **Not clinically validated:** the individual studies are published, but this combination of methods has not been
  validated. Not medical advice.
- **Plugins are trusted code, not a sandbox.** A single call that hangs is not interrupted.
- **Values can differ slightly from the Google Health app.** The API is the source used.
- **Runs** count toward the race plan only when they are logged as type Run.
- **Units:** the report and dashboard show metric units for now.

## Roadmap

- Linux support (CI runs Ubuntu; not yet verified).
- Further work follows real use; ideas are welcome as issues.

## Contributing

Issues and pull requests are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) and the
[code of conduct](CODE_OF_CONDUCT.md) first. Report security issues privately, as described in
[SECURITY.md](SECURITY.md).

## License

Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).

Built by Vaikartana ([@v4ikartana](https://github.com/v4ikartana))

TDQS

A3.6/5.0

Scored across 17 tools

Disambiguation4/5

Most tools target clearly distinct resources or actions (sync, overview, sleep, running, notes, profile, SQL). There is mild overlap among the retrieval tools (get_trends, get_sleep, get_activity, get_body_age all return health series), but descriptions and metric scoping make the boundaries clear enough.

Naming Consistency4/5

All tool names use snake_case, and the dominant pattern is verb_noun (get_overview, get_sleep, save_coach_note, update_coach_note, set_profile). A few nouns-first or verb-first outliers (data_status, refresh_data, describe_schema, query_sql) break the strict pattern but remain readable and consistent in style.

Tool Count4/5

17 tools is slightly on the heavy side but each maps to a coherent capability of a full health-tracking domain (sync, domain fetchers, notes, profile, evidence, introspection). Nothing appears redundant or purely filler.

Completeness4/5

Covers the full lifecycle well: sync, per-domain reads (sleep/activity/running/trends/body age), journal and coach-note write/read/update, profile config, plus evidence and SQL introspection. Minor gaps exist (no hard delete for journal entries or coach notes, deletion only via status update), but agents can work around these.

Maintenance

ActivityMaintained
ResponsivenessNo issues