{
  "markdown": "# WHOOP MCP Server\n\n[![CI](https://github.com/AshwanthramKL/whoop-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/AshwanthramKL/whoop-mcp/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/whoop-mcp.svg)](https://pypi.org/project/whoop-mcp/)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)\n[![MCP](https://img.shields.io/badge/MCP-compatible-6e6eff.svg)](https://modelcontextprotocol.io)\n[![Tests](https://img.shields.io/badge/tests-198%20passing-brightgreen.svg)](./tests)\n\nA local Model Context Protocol (MCP) server that gives an LLM **read-only**\naccess to your WHOOP fitness data. Authentication is direct OAuth against\nyour own WHOOP developer app — there is no third-party proxy in the path.\nAll records are mirrored into a local SQLite cache at\n`~/.whoop-mcp-server/whoop.db`, and **no data ever leaves your machine**\nexcept for the authenticated calls the server itself makes to the WHOOP\nv2 API.\n\nCurrent version: **0.8.5** — see [CHANGELOG.md](./CHANGELOG.md).\n\n> **Pre-1.0 status.** The API surface (tool names, response shapes, error\n> codes) is stabilizing but not frozen. Breaking changes may land in\n> `0.x` minor bumps during dogfooding. Patch bumps (`0.8.x`) are\n> bugfix-only. 1.0.0 will be cut when the surface has been stable for\n> 2+ weeks of real use. Pin the minor version in CI if you're building\n> on top of this.\n\n> **Just want to try it?** Copy the prompt in\n> [docs/AGENT_INSTALL_PROMPT.md](./docs/AGENT_INSTALL_PROMPT.md), paste\n> it into Claude Code / Claude Desktop / Cursor / Windsurf, and your\n> agent will do the install end-to-end.\n>\n> **Building on this repo?** Start with [AGENTS.md](./AGENTS.md) and\n> [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) — they're the\n> load-bearing conventions and one-page system map.\n\n## Table of contents\n\n- [What this is](#what-this-is)\n- [Install (one line via uvx)](#install-one-line-via-uvx)\n- [Install with your agent](#install-with-your-agent)\n- [Install from source](#install-from-source-for-development)\n- [Analysis skill: whoop-insights](#analysis-skill-whoop-insights)\n- [Quick start](#quick-start)\n- [Tool catalog](#tool-catalog)\n- [MCP resources](#mcp-resources)\n- [Data model](#data-model)\n- [Sync model](#sync-model)\n- [Event feed](#event-feed)\n- [Exports](#exports)\n- [What's not supported](#whats-not-supported)\n- [Updating](#updating)\n- [Operations](#operations)\n- [Security and privacy](#security-and-privacy)\n- [Development](#development)\n- [For agents building on this repo](#for-agents-building-on-this-repo)\n- [Versioning](#versioning)\n\n## What this is\n\nThe WHOOP MCP Server is a small Python process that speaks MCP over stdio.\nIt exposes WHOOP v2 data (profile, body measurement, cycles, recoveries,\nsleeps, workouts) to any MCP-capable client — primarily Claude Desktop and\nClaude Code. The server is read-only. It authenticates with WHOOP using an\nOAuth app you register yourself, so your tokens never travel through a\nthird-party server. The WHOOP records you fetch are written to a local\nSQLite cache (mode `0o600`) so subsequent reads are free and offline, and\nthe cache file never leaves your machine.\n\n## Install (one line via uvx)\n\n```bash\n# 1. Create a WHOOP developer app (one-time, free, self-serve):\n#    https://developer-dashboard.whoop.com/apps/create\n#    Redirect URI:  http://localhost:8000/callback\n#    Scopes:        read:profile read:body_measurement read:recovery\n#                   read:cycles read:sleep read:workout offline\n\n# 2. One-shot OAuth — opens browser, catches callback, saves encrypted tokens.\nWHOOP_CLIENT_ID=\"<your client id>\" \\\nWHOOP_CLIENT_SECRET=\"<your client secret>\" \\\n  uvx --from whoop-mcp whoop-mcp-oauth\n\n# 3. Register with Claude Code. The --env flags are required so the\n#    server can refresh tokens when the 1-hour access token expires.\nclaude mcp add whoop --scope user \\\n  --env WHOOP_CLIENT_ID=\"$WHOOP_CLIENT_ID\" \\\n  --env WHOOP_CLIENT_SECRET=\"$WHOOP_CLIENT_SECRET\" \\\n  -- uvx --from whoop-mcp whoop-mcp\n```\n\nNo clone, no venv, no absolute paths. Don't have `uv`? Install it once:\n`curl -LsSf https://astral.sh/uv/install.sh | sh` (or `brew install uv`).\n\n## Install with your agent\n\nPaste the prompt in [docs/AGENT_INSTALL_PROMPT.md](./docs/AGENT_INSTALL_PROMPT.md)\ninto any MCP-aware agent (Claude Code, Claude Desktop, Cursor,\nWindsurf, Zed, Aider). The agent will run the install for you.\n\n## Install from source (for development)\n\n```bash\ngit clone https://github.com/AshwanthramKL/whoop-mcp.git\ncd whoop-mcp\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt -r requirements-dev.txt\n.venv/bin/pytest -q   # 183 tests, ~6s, all respx-mocked\n```\n\nThen register with `-- /abs/path/to/whoop-mcp/.venv/bin/python /abs/path/to/whoop-mcp/src/whoop_mcp_server.py`.\n\nFor Claude Desktop, add the equivalent entry to\n`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS),\npointing `command` at the venv Python and `args` at `src/whoop_mcp_server.py`.\nRun `./scripts/fresh_install_check.sh` if you want a clean-env smoke\nof the install path end-to-end (minus the browser OAuth flow).\n\n## Quick start\n\nOnce the server is registered, try these prompts:\n\n1. **Sync the cache.** \"Sync my WHOOP data from the last 30 days.\"\n   Claude calls `sync_whoop()` and reports per-resource counts.\n2. **Inspect a day.** \"Give me yesterday's WHOOP daily summary.\"\n   Claude calls `get_whoop_daily_summary(date=\"YYYY-MM-DD\")` and shows a\n   joined cycle + recovery + primary sleep + workouts record.\n3. **Export.** \"Export all my cached workouts to `~/whoop-workouts.csv`.\"\n   Claude calls `export_whoop(kind=\"workouts\", format=\"csv\", path=\"...\")`.\n\n## Analysis skill: whoop-insights\n\nBundled with the repo: [skills/whoop-insights](./skills/whoop-insights/SKILL.md).\n\n**Install (one command):**\n\n```bash\nwhoop-mcp-install-skills            # writes to ~/.claude/skills/\n```\n\nThat fetches the latest skill from this repo's `main` branch and drops\nit where Claude Code (and Cursor / Windsurf / Zed) auto-discovers\nuser-scope skills. Restart your MCP client session and ask:\n\n> *\"How am I doing? Run the whoop-insights skill.\"*\n> *\"Generate my weekly WHOOP report.\"*\n> *\"Am I overtraining? Check the last 30 days.\"*\n\nThe skill pulls 30 days from the cache, computes personal baselines\n(HRV, recovery, sleep, strain), flags anomalies with evidence, runs two\ncorrelations (sleep→next-day-recovery, strain→next-day-recovery), and\noptionally generates a self-contained HTML dashboard with Chart.js\nvisualizations. Every claim cites a specific date and a specific number\n— the skill is instructed never to fabricate.\n\n**For repo-native developers** iterating on the skill itself:\n`whoop-mcp-install-skills --source ./skills` installs from your local\nworking tree instead of GitHub.\n\n## Tool catalog\n\n17 tools. All list tools accept `start` / `end` as ISO-8601 and auto-paginate.\nEvery read tool takes `fresh: bool = False` — pass `True` to bypass the\ncache and hit the WHOOP API, write-through to cache, and return the live\nresponse. Errors are returned as a structured `{\"error\": {...}}` envelope;\ntools never raise.\n\n| Tool | What it does | Key parameters |\n|------|--------------|----------------|\n| `get_whoop_auth_status` | Report OAuth token status. Call first if other tools return `AUTH_FAILED`. | — |\n| `get_whoop_profile` | Authenticated user's WHOOP profile (name, email). | `fresh` |\n| `get_whoop_body_measurement` | Latest body measurements: height, weight, max HR. | `fresh` |\n| `list_whoop_cycles` | Physiological cycles in a time window. | `start`, `end`, `limit`, `fresh` |\n| `get_whoop_cycle` | One cycle by integer ID. | `cycle_id`, `fresh` |\n| `get_whoop_cycle_sleep` | Sleep record tied to a given cycle. | `cycle_id`, `fresh` |\n| `get_whoop_cycle_recovery` | Recovery record tied to a given cycle. | `cycle_id`, `fresh` |\n| `list_whoop_recoveries` | Recoveries (HRV / RHR / recovery score) in a window. | `start`, `end`, `limit`, `fresh` |\n| `list_whoop_sleeps` | Sleep activities incl. naps in a window. | `start`, `end`, `limit`, `fresh` |\n| `get_whoop_sleep` | One sleep activity by UUID. | `sleep_id`, `fresh` |\n| `list_whoop_workouts` | Workouts with zone durations (seconds). | `start`, `end`, `limit`, `fresh` |\n| `get_whoop_workout` | One workout by UUID. | `workout_id`, `fresh` |\n| `get_whoop_daily_summary` | Joined cycle + recovery + primary sleep + workouts for a UTC date. | `date` |\n| `sync_whoop` | Refresh cache from WHOOP API. Idempotent, incremental by default. | `full`, `since`, `resources` |\n| `get_whoop_events` | Chronological \"what's new\" feed across cached resources. | `since`, `until`, `resources`, `limit` |\n| `export_whoop` | Dump cached records to CSV / JSONL / Parquet. | `kind`, `format`, `path`, `start`, `end`, `overwrite` |\n| `health_check` | Composite status (auth, API, cache, schema). Never raises. | `live` |\n\nError codes: `AUTH_FAILED`, `RATE_LIMITED`, `NOT_FOUND`, `UPSTREAM_ERROR`,\n`VALIDATION_ERROR`, `CACHE_ERROR`, `CACHE_EMPTY`, `FILE_EXISTS`,\n`EXPORT_ERROR`, `SYNC_ERROR`.\n\n## MCP resources\n\nThe cache is also exposed as read-only MCP resources, so the client can\nbrowse date slices without invoking a tool.\n\n| URI | Content |\n|-----|---------|\n| `whoop://db/cycles/{start}/{end}` | Cached cycles in `[start, end)` (dates `YYYY-MM-DD`). |\n| `whoop://db/recoveries/{start}/{end}` | Cached recoveries. |\n| `whoop://db/sleeps/{start}/{end}` | Cached sleeps (including naps). |\n| `whoop://db/workouts/{start}/{end}` | Cached workouts. |\n| `whoop://db/profile` | Latest profile snapshot. |\n| `whoop://db/body_measurement` | Latest body_measurement snapshot. |\n| `whoop://db/sync_runs/{limit}` | Most recent sync audit rows. |\n| `whoop://db/events/{since}` | Event feed since `since`, `until` = now. |\n| `whoop://db/events/{since}/{until}` | Event feed for explicit window. |\n\nAll resources return `application/json`. Bad inputs return the same\n`{\"error\": {\"code\",\"message\"}}` envelope used by tools.\n\n## Data model\n\nResponses are **flattened**: the WHOOP `score` wrapper is lifted, milliseconds\nbecome seconds (`*_seconds`, 1 decimal), kilojoules become calories\n(`calories`, rounded int), heart-rate keys are renamed to `avg_hr_bpm` /\n`max_hr_bpm`, and sleep stages are named `deep_sleep_seconds`,\n`rem_sleep_seconds`, `light_sleep_seconds`, `awake_seconds`,\n`in_bed_seconds`. Raw `user_id`, `v1_id`, and per-record `created_at` are\ndropped.\n\n**`score_state` convention.** WHOOP scores are not always computed. When\n`score_state != \"SCORED\"` (e.g. `PENDING_SCORE`, `UNSCORABLE`) every\nderived score field is `null` and the top-level `score_state` is preserved\nso callers know why.\n\nOne flattened record per resource:\n\n```json\n// Cycle\n{\n  \"id\": 123456,\n  \"start\": \"2026-04-20T04:00:00.000Z\",\n  \"end\": \"2026-04-21T04:00:00.000Z\",\n  \"timezone_offset\": \"+00:00\",\n  \"score_state\": \"SCORED\",\n  \"strain\": 12.4,\n  \"avg_hr_bpm\": 62,\n  \"max_hr_bpm\": 168,\n  \"calories\": 2810\n}\n```\n\n```json\n// Recovery\n{\n  \"cycle_id\": 123456,\n  \"sleep_id\": \"bb68db7b-...\",\n  \"score_state\": \"SCORED\",\n  \"recovery_score\": 74,\n  \"resting_heart_rate_bpm\": 48,\n  \"hrv_rmssd_ms\": 87.3,\n  \"spo2_pct\": 97.4,\n  \"skin_temp_c\": 33.1,\n  \"user_calibrating\": false\n}\n```\n\n```json\n// Sleep\n{\n  \"id\": \"bb68db7b-...\",\n  \"cycle_id\": 123456,\n  \"start\": \"2026-04-20T03:10:00.000Z\",\n  \"end\": \"2026-04-20T10:42:00.000Z\",\n  \"nap\": false,\n  \"score_state\": \"SCORED\",\n  \"sleep_performance_pct\": 88.0,\n  \"in_bed_seconds\": 27120.0,\n  \"light_sleep_seconds\": 12540.0,\n  \"rem_sleep_seconds\": 5280.0,\n  \"deep_sleep_seconds\": 6840.0,\n  \"awake_seconds\": 1080.0\n}\n```\n\n```json\n// Workout\n{\n  \"id\": \"a91f...\",\n  \"start\": \"2026-04-20T17:00:00.000Z\",\n  \"end\": \"2026-04-20T17:48:00.000Z\",\n  \"sport_name\": \"Running\",\n  \"score_state\": \"SCORED\",\n  \"strain\": 9.3,\n  \"avg_hr_bpm\": 142,\n  \"max_hr_bpm\": 176,\n  \"calories\": 511,\n  \"distance_meter\": 8030.0,\n  \"zone_durations_seconds\": {\n    \"zone_zero\": 0.0, \"zone_one\": 120.0, \"zone_two\": 900.0,\n    \"zone_three\": 1440.0, \"zone_four\": 420.0, \"zone_five\": 0.0\n  }\n}\n```\n\n## Sync model\n\n- **Cache-first reads.** Every list/get tool reads from SQLite by default.\n  An empty window transparently triggers a targeted sync and re-reads.\n- **`fresh=True`** bypasses the cache, hits the WHOOP API, write-throughs\n  the result into the cache, and returns the live response.\n- **Incremental sync** uses `MAX(updated_at)` per resource as the cursor.\n  A fresh DB falls back to the last 90 days. `sync_whoop(full=True)` pulls\n  from 2010-01-01 for every resource (do this once on a brand-new cache).\n- **Idempotent.** Re-running `sync_whoop` with no new upstream data is a\n  no-op. Combined with snapshot hash dedupe (below), this means the event\n  feed stays quiet.\n- **Snapshot hash dedupe.** `profile` and `body_measurement` are singleton\n  snapshots. Before writing, the server compares SHA-256 of the canonical\n  raw payload against the stored blob. Byte-identical payloads do not\n  advance `updated_at`, so no spurious events are generated.\n\n## Event feed\n\n`get_whoop_events(since, until=None, resources=None, limit=500)` is a\nchronological feed across all cached resources — pure cache read, no API\ncalls. The window is **exclusive on both ends**:\n`updated_at > since AND updated_at < until`. The strict `since` bound\nmeans you can feed a returned cursor back in as the next `since` without\nre-seeing a row.\n\nEach event wraps a flat record:\n\n```json\n{\"resource\": \"sleeps\",\n \"id\": \"bb68db7b-...\",\n \"updated_at\": \"2026-04-20T14:12:33.123Z\",\n \"record\": { \"...flat sleep...\" }}\n```\n\nResponse:\n\n```json\n{\"status\": \"success\", \"count\": 17,\n \"since\": \"...\", \"until\": \"...\",\n \"events\": [...],\n \"next_cursor\": null | \"<opaque-base64>\"}\n```\n\n`next_cursor` is an **opaque** base64 encoding of\n`updated_at|resource|id` for the last returned event. Pass it back as\n`since` to continue. The full triple is used as a tie-broken lower bound,\nso events sharing an `updated_at` are never skipped at a pagination\nboundary. Plain ISO-8601 strings as `since` still work (M5 compat).\n\nAlso exposed as MCP resources — see the [MCP resources](#mcp-resources)\ntable above.\n\n## Exports\n\n`export_whoop(kind, format, path, start=None, end=None, overwrite=False)`\nwrites flat cached records to disk. Pure data layer — never hits the API.\nRun `sync_whoop()` first.\n\n- **`kind`**: `cycles` | `recoveries` | `sleeps` | `workouts` | `all`.\n  `\"all\"` writes one file per resource (`cycles.*`, `recoveries.*`, …)\n  into the given directory.\n- **`format`**: `csv` (RFC 4180, alphabetically sorted header, nested\n  values JSON-encoded), `jsonl` (one record per line, sorted keys), or\n  `parquet` (pyarrow, snappy).\n- **`overwrite`**: default `False`. If the destination has content,\n  returns `FILE_EXISTS`. `True` replaces silently.\n\nAn empty window still yields a file (header-only CSV / empty JSONL /\nempty Parquet) so downstream tooling sees a consistent artifact.\n\n## What's not supported\n\nSpelled out so nobody wastes an issue.\n\n| Ask | Why not |\n|-----|---------|\n| `claude.ai` (the web app) | We ship as a **stdio** MCP (local subprocess). `claude.ai` wants a remote HTTPS MCP. A hosted deployment would also break our single-user / local-only threat model. |\n| Mobile Claude apps | Same stdio constraint. |\n| Writing back to WHOOP (logging activities, updating weight, etc.) | Read-only by design. Adding writes would require reverse-engineered endpoints outside WHOOP's stable v2 API — too brittle and ToS-risky. See [`jd1207/whoop-mcp`](https://github.com/jd1207/whoop-mcp) if you need that. |\n| Multi-user / shared server | One user, one WHOOP account, one machine. The cache + encrypted tokens live under `~/.whoop-mcp-server/`; there's no tenancy model. |\n| More than 10 users per WHOOP dev app | WHOOP's dev-app cap is 10 users until app-level approval. Each user should create their own dev app — it's free and self-serve. |\n| Push notifications / webhooks | Event feed is poll-driven from the cache. For proactive alerts, run `sync_whoop` + `get_whoop_events` on a cron from whatever scheduler you already have. |\n| Python < 3.10 | The code uses `X \\| Y` union syntax. `pip install` will refuse on older Pythons. |\n| Windows-specific install paths | Code works, but OAuth callback (`localhost:8000`) and path handling haven't been dogfooded on Windows. Ubuntu + macOS are the currently-tested surface. |\n\n## Updating\n\nMCP servers don't auto-update. When you installed is when you pinned.\nHow to pick up a new release depends on how you installed:\n\n| Install path | How to update |\n|---|---|\n| `uvx --from whoop-mcp whoop-mcp` | `uvx --refresh --from whoop-mcp whoop-mcp`, or `uv cache clean whoop-mcp` and re-invoke |\n| `uvx --from whoop-mcp@latest whoop-mcp` | Update happens on next cache miss (no manual step) |\n| `pipx install whoop-mcp` | `pipx upgrade whoop-mcp` |\n| `pip install whoop-mcp` | `pip install --upgrade whoop-mcp` |\n| Git clone + venv | `git pull && pip install -r requirements.txt` |\n\nRun `health_check()` afterwards — its `pypi_update_available` component\nwill confirm the version your MCP client is now running and whether a\nnewer one is published.\n\nBreaking-change policy: while the project is `0.x`, minor bumps\n(`0.8.x` → `0.9.0`) may include breaking changes — always called out in\nthe `### Changed` section of [CHANGELOG.md](./CHANGELOG.md). Patch\nbumps (`0.8.3` → `0.8.5`) are bugfix-only. Once we cut `1.0.0`,\nbreaking changes require a major bump. Pin to a known-good `0.x.y` if\nyou can't absorb churn.\n\n## Operations\n\n- **Logs.** Stderr (always, structured JSON) plus a rotating file at\n  `~/.whoop-mcp-server/logs/whoop-mcp.log` (~1 MB per file, 5 backups).\n  Env overrides: `WHOOP_LOG_LEVEL` (default `INFO`), `WHOOP_LOG_FILE`\n  (path; empty string disables the file handler), `WHOOP_LOG_JSON`\n  (default `true`).\n- **`health_check(live=True)`** returns a dict with five component\n  checks (`auth`, `api_reachable`, `cache_readable`, `cache_writable`,\n  `schema_version`) plus an overall verdict `healthy | degraded |\n  unhealthy`. `live=False` skips the network probe.\n- **Token refresh.** Refresh tokens are used automatically when the\n  access token is within 5 minutes of expiry. An async refresh lock\n  prevents stampedes when multiple in-flight requests discover the same\n  expired token. If refresh fails beyond recovery, re-run\n  `whoop-mcp-oauth` (after `pip install`) or\n  `.venv/bin/python src/setup_direct_oauth.py` (from source).\n- **Rate limiting.** The client respects `Retry-After` on 429s and\n  retries with exponential backoff. After the retry budget, the call\n  surfaces as `RATE_LIMITED` — tools never raise.\n\n## Security and privacy\n\nEverything runs locally. OAuth tokens are encrypted at rest in\n`~/.whoop-mcp-server/tokens.json` with a key at\n`~/.whoop-mcp-server/.encryption_key`. The SQLite cache file is created\nwith mode `0o600`. No third-party services — the only outbound network\ncalls are directly to `api.prod.whoop.com`. Logs do **not** include\ntokens, refresh tokens, the client secret, or raw WHOOP response bodies.\nSee [PRIVACY.md](./PRIVACY.md) for the complete inventory of what the\nserver reads, writes, sends, and logs; and how to delete everything\n(`rm -rf ~/.whoop-mcp-server`).\n\n## Development\n\n```bash\n# Run the full test suite (183 tests).\n.venv/bin/pytest -q\n\n# Re-record fixtures (live calls, requires creds in env).\n.venv/bin/python tests/record_fixtures.py\n\n# Fresh-install smoke test (clones current HEAD into a tempdir and\n# verifies the install path end-to-end, minus the browser OAuth flow).\nbash scripts/fresh_install_check.sh\n```\n\n**Adding a new tool.** Add the implementation under `src/whoop_mcp_server.py`\nwith an `@mcp.tool()` decorator, a Pydantic model in `src/whoop_models.py`\nif the response has a new shape, a cache table in `src/whoop_store.py` if\nthe resource is persisted, and tests in `tests/`. Keep the error envelope\n(`_error_payload` / `_map_error`) — tools never raise.\n\n**Release flow.** Bump `src/__version__.py` (single source of truth) →\nadd a new top section to [CHANGELOG.md](./CHANGELOG.md) in\nKeep-a-Changelog format → verify `SERVER_VERSION` in\n`whoop_mcp_server.py` picks up the new value (the `test_version_import`\ntest prevents drift) → update `version` in `pyproject.toml` → tag\n`vX.Y.Z` on `main`.\n\n## For agents building on this repo\n\nStart here:\n\n- **[AGENTS.md](./AGENTS.md)** — load-bearing conventions. File layout,\n  error-envelope discipline, TDD loop, what-not-to-do, release flow.\n  Read before editing anything.\n- **[docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md)** — one-page system\n  map. Four-layer diagram, read/write/sync/event paths, invariants.\n- **[docs/AGENT_INSTALL_PROMPT.md](./docs/AGENT_INSTALL_PROMPT.md)** —\n  the copy-pasteable prompt end users give their agent to install.\n\nHuman contributors: [CONTRIBUTING.md](./CONTRIBUTING.md) has the short\nversion. The test suite runs in ~6s (`.venv/bin/pytest -q`); every HTTP\ncall is respx-mocked so you can iterate offline.\n\n## Versioning\n\nCurrent version: **0.8.5** (see `src/__version__.py`). Semantic\nversioning. Full history: [CHANGELOG.md](./CHANGELOG.md).\n\n## Credits\n\nForked from [RomanEvstigneev/whoop-mcp-server](https://github.com/RomanEvstigneev/whoop-mcp-server)\n(v0.1.x). Substantially rewritten starting at v0.2.0 to use direct WHOOP\nOAuth (no third-party proxy), the WHOOP v2 API, Pydantic-flattened\nresponses, a local SQLite cache with incremental sync, cache-first\nreads, an event feed, exports (CSV/JSONL/Parquet), a `health_check`\ntool, structured JSON logging, and 190+ tests (190 as of 0.8.5). Licensed MIT — see\n[LICENSE](./LICENSE) for both copyright lines.\n\n---\n\n<!-- MCP Registry ownership proof. Do not remove. -->\n\nmcp-name: io.github.AshwanthramKL/whoop-mcp\n",
  "bytes": 21823,
  "sha": "6aa5175b810740c12ef9097beaa6a3d0c851fbbcc404753c16421eed5777ac0b",
  "repo_slug": "ashwanthramkl/whoop-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_ashwanthramkl_whoop_mcp_b505cb16/readme"
}