Skip to main content
ClaudeWave

Standalone WHOOP MCP server: OAuth2 client, syncs recovery/sleep/workouts/cycles to local Postgres, MCP tools over stdio

MCP ServersRegistry oficial0 estrellas0 forksPythonMITActualizado today
ClaudeWave Trust Score
87/100
Trusted
Passed
  • Open-source license (MIT)
  • Actively maintained (<30d)
  • Clear description
  • Documented (README)
Last scanned: 9/14/2026
Install in Claude Code / Claude Desktop
Method: UVX (Python) · whoop-postgres-mcp
Claude Code CLI
claude mcp add whoop-mcp -- uvx whoop-postgres-mcp
claude_desktop_config.json (Claude Desktop)
{
  "mcpServers": {
    "whoop-mcp": {
      "command": "uvx",
      "args": ["whoop-postgres-mcp"],
      "env": {
        "WHOOP_CLIENT_SECRET": "<whoop_client_secret>",
        "WHOOP_DB_URL": "<whoop_db_url>",
        "WHOOP_TEST_DB_URL": "<whoop_test_db_url>",
        "POSTGRES_PASSWORD": "<postgres_password>"
      }
    }
  }
}
1. Run the command above in your terminal (Claude Code), or paste the JSON config into claude_desktop_config.json (Claude Desktop).
2. Replace any <placeholder> values with your API keys or paths.
3. Restart Claude. The MCP server and its tools appear automatically.
Detected environment variables
WHOOP_CLIENT_SECRETWHOOP_DB_URLWHOOP_TEST_DB_URLPOSTGRES_PASSWORD
Casos de uso

Resumen de MCP Servers

# whoop-mcp

<!-- mcp-name: io.github.cunicopia-dev/whoop-postgres-mcp -->

![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)
![License: MIT](https://img.shields.io/badge/license-MIT-green)
![storage: Postgres](https://img.shields.io/badge/storage-Postgres-4169E1?logo=postgresql&logoColor=white)
![MCP](https://img.shields.io/badge/MCP-ready-FF6F00)

**An [MCP](https://modelcontextprotocol.io) server that syncs your WHOOP data
into Postgres and lets AI agents query it.**

WHOOP's public API is rate-limited, cursor-paginated, and returns one record
at a time. That is fine for a sync job and terrible for an agent that wants to
answer "how does my HRV this week compare to my baseline?". So this server
splits the job in two:

* a **sync** path that pulls cycles, recovery, sleep, workouts, and body
  measurements from the WHOOP API into a documented Postgres schema, keeping
  the raw JSON of every record alongside the typed columns;
* a **query** path of MCP tools that read Postgres only. They are fast, never
  hit WHOOP's rate limits, and keep working offline once data is synced.

Your data lands in a database you own, in a schema you can read with any SQL
client, and nothing leaves your machine except calls to WHOOP itself.

> Python 3.12+ · MIT · stdio MCP server + CLI · Postgres storage

---

## Contents

- [Tools](#tools)
- [Install](#install)
- [Quickstart](#quickstart)
- [Configuration](#configuration)
- [Register with an MCP client](#register-with-an-mcp-client)
- [How sync works](#how-sync-works)
- [Security model](#security-model)
- [Development](#development)
- [Project layout](#project-layout)
- [License](#license)

---

## Tools

Two write-side tools talk to the WHOOP API; everything else reads Postgres only.

| Tool | Args | What it does |
| --- | --- | --- |
| `whoop_auth_url` | none | Returns the OAuth authorization URL to open in a browser. |
| `whoop_connect` | `code` | Exchanges the OAuth code for tokens, stores them, returns the connected user. |
| `whoop_sync` | `days=7`, `full=false` | Pulls data from WHOOP into Postgres. Returns per-collection counts and errors. |
| `whoop_status` | none | Per-collection sync health: last sync, watermark, row count, last error. |
| `whoop_overview` | `days=7` | Latest recovery and vitals vs. 30-day baseline, last night's sleep, recent workouts and strain. |
| `whoop_recovery` | `days=7`, `limit=50` | Recent recovery scores, resting HR, HRV, SpO2, skin temperature. |
| `whoop_sleep` | `days=7`, `limit=50` | Recent sleeps and naps with stage breakdown, need, performance, efficiency. |
| `whoop_workouts` | `days=14`, `limit=50` | Recent workouts with strain, HR, energy, distance, and zone minutes. |
| `whoop_cycles` | `days=7`, `limit=50` | Recent physiological cycles (WHOOP days) with day strain and HR. |
| `whoop_baseline` | none | 30-day mean and standard deviation per vital. |

`days` is capped at 365 and `limit` at 200. All tools return JSON text.

## Install

```bash
uv tool install whoop-postgres-mcp      # or: pipx install whoop-postgres-mcp
```

Or run it without installing:

```bash
uvx whoop-postgres-mcp --help
```

You also need a Postgres database (any recent version; 14+ is fine) and a
WHOOP developer app. [docs/SETUP.md](docs/SETUP.md) walks through both.

## Quickstart

```bash
export WHOOP_CLIENT_ID=...
export WHOOP_CLIENT_SECRET=...
export WHOOP_REDIRECT_URI=http://localhost:8765/callback
export WHOOP_DB_URL=postgresql://whoop:whoop@localhost:5432/whoop

whoop-mcp init-db          # create the `whoop` schema (idempotent)
whoop-mcp auth-url         # print the URL to visit; approve access in a browser
whoop-mcp connect <code>   # paste the `code` from the redirect URL
whoop-mcp sync --all       # first backfill; later runs: whoop-mcp sync --days 7
whoop-mcp                  # serve MCP over stdio
```

The same flow is available as MCP tools (`whoop_auth_url`, `whoop_connect`,
`whoop_sync`) so an agent can drive the whole setup.

## Configuration

All configuration is by environment variable. The server validates every
variable at startup and exits with a message naming each missing one.

| Variable | Required | Meaning |
| --- | --- | --- |
| `WHOOP_CLIENT_ID` | yes | Client ID of your WHOOP developer app. |
| `WHOOP_CLIENT_SECRET` | yes | Client secret of your WHOOP developer app. |
| `WHOOP_REDIRECT_URI` | yes | Redirect URI, exactly as registered on the app. Any URL works; the server does not listen on it. You copy the `code` from the address bar. |
| `WHOOP_DB_URL` | yes | Postgres DSN, e.g. `postgresql://user:pass@host:5432/db`. Tokens and data live here. |

## Register with an MCP client

Claude Code:

```bash
claude mcp add whoop -e WHOOP_CLIENT_ID=... -e WHOOP_CLIENT_SECRET=... \
  -e WHOOP_REDIRECT_URI=http://localhost:8765/callback \
  -e WHOOP_DB_URL=postgresql://whoop:whoop@localhost:5432/whoop \
  -- uvx whoop-postgres-mcp
```

Claude Desktop (`claude_desktop_config.json`) or any client that takes a
stdio command:

```json
{
  "mcpServers": {
    "whoop": {
      "command": "uvx",
      "args": ["whoop-postgres-mcp"],
      "env": {
        "WHOOP_CLIENT_ID": "...",
        "WHOOP_CLIENT_SECRET": "...",
        "WHOOP_REDIRECT_URI": "http://localhost:8765/callback",
        "WHOOP_DB_URL": "postgresql://whoop:whoop@localhost:5432/whoop"
      }
    }
  }
}
```

## How sync works

WHOOP's collection endpoints filter on when a record *occurred*, not when it
was last modified, and records change after creation (a sleep is created as
`PENDING_SCORE` and scored later). The sync therefore:

1. always re-fetches the last `days` days, which catches late re-scores;
2. tracks a per-collection watermark (`newest_updated_at` in
   `whoop_sync_state`). If the last sync is older than `days`, the window is
   stretched back to the watermark minus a two-day lookback so a gap never
   leaves a hole;
3. upserts every record on its natural key (`id`, or `cycle_id` for recovery),
   so overlapping windows are harmless and re-running is safe;
4. records per-collection errors in `whoop_sync_state` and carries on with the
   other collections rather than aborting the run.

`full=true` (or `whoop-mcp sync --all`) drops the lower bound and walks the
account's entire history. Body measurements are a single current record and are
refreshed on every sync.

Token refresh is proactive (five minutes before expiry) and serialized through
a Postgres advisory lock, so the MCP server and a cron-driven `whoop-mcp sync`
can share one token row without racing.

The full data model is documented in [docs/SCHEMA.md](docs/SCHEMA.md).

## Security model

* **Database access is token access.** OAuth tokens are stored in plaintext in
  `whoop.whoop_tokens`. Anyone who can read that table can call the WHOOP API
  as you until the refresh token is revoked. Restrict database grants
  accordingly and treat `WHOOP_DB_URL` as a secret.
* **Read tools never reach the network.** Only `whoop_connect` and `whoop_sync`
  talk to WHOOP. Everything else is a SQL query against your database.
* **Nothing is written outside Postgres.** No files, no caches, no telemetry.
* **Scopes are read-only.** The app requests `read:*` scopes plus `offline` for
  refresh tokens. It cannot modify anything in your WHOOP account.
* To revoke access, delete the row from `whoop_tokens` and remove the app from
  your WHOOP account settings.

## Development

```bash
git clone https://github.com/cunicopia-dev/whoop-mcp
cd whoop-mcp
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"

ruff check .
mypy src
pytest                       # pure-logic tests
WHOOP_TEST_DB_URL=postgresql://whoop:whoop@localhost:5432/whoop pytest   # + live DB tests
```

The live tests apply the schema and truncate every table in the target
database before each test. Point them at a scratch database.

A throwaway Postgres for local testing:

```bash
docker run -d --rm --name whoop-pg -e POSTGRES_USER=whoop -e POSTGRES_PASSWORD=whoop \
  -e POSTGRES_DB=whoop -p 5432:5432 postgres:16-alpine
```

## Project layout

```
src/whoop_mcp/
  config.py     environment variables, validated at startup
  auth.py       OAuth2 flow, token persistence, locked refresh
  client.py     httpx client: pagination, backoff, Retry-After
  schema.sql    the Postgres DDL (applied by `whoop-mcp init-db`)
  db.py         connection + schema helpers
  sync.py       incremental sync with per-collection watermarks
  queries.py    read-side SQL behind the MCP tools
  server.py     MCP server over stdio + CLI entry point
docs/
  SETUP.md      WHOOP app registration, OAuth walkthrough, DB init, first sync
  SCHEMA.md     column-by-column data model
tests/          config, schema, client, auth, sync, and stdio protocol tests
```

## License

MIT. See [LICENSE](LICENSE).

Lo que la gente pregunta sobre whoop-mcp

¿Qué es cunicopia-dev/whoop-mcp?

+

cunicopia-dev/whoop-mcp es mcp servers para el ecosistema de Claude AI. Standalone WHOOP MCP server: OAuth2 client, syncs recovery/sleep/workouts/cycles to local Postgres, MCP tools over stdio Tiene 0 estrellas en GitHub y su última actualización registrada es del 2026-09-13.

¿Cómo se instala whoop-mcp?

+

Puedes instalar whoop-mcp clonando el repositorio (https://github.com/cunicopia-dev/whoop-mcp) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.

¿Es seguro usar cunicopia-dev/whoop-mcp?

+

Nuestro agente de seguridad ha analizado cunicopia-dev/whoop-mcp y le ha asignado un Trust Score de 87/100 (tier: Trusted). Revisa el desglose completo de comprobaciones superadas y flags en esta página.

¿Quién mantiene cunicopia-dev/whoop-mcp?

+

cunicopia-dev/whoop-mcp es mantenido por cunicopia-dev. La última actividad registrada en GitHub es del 2026-09-13, con 0 issues abiertos.

¿Hay alternativas a whoop-mcp?

+

Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.

Despliega whoop-mcp en tu cloud

Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.

¿Mantienes este repo? Añade un badge a tu README

Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.

Featured on ClaudeWave: cunicopia-dev/whoop-mcp
[![Featured on ClaudeWave](https://claudewave.com/api/badge/cunicopia-dev-whoop-mcp)](https://claudewave.com/repo/cunicopia-dev-whoop-mcp)
<a href="https://claudewave.com/repo/cunicopia-dev-whoop-mcp"><img src="https://claudewave.com/api/badge/cunicopia-dev-whoop-mcp" alt="Featured on ClaudeWave: cunicopia-dev/whoop-mcp" width="320" height="64" /></a>

Más MCP Servers

Alternativas a whoop-mcp