Read-only MCP server bringing Whoop health data (recovery, sleep, workouts, cycles) into Claude Desktop — OAuth 2.0, minimal dependencies, no telemetry.
Whoop MCP
Local MCP server that exposes your Whoop data (recovery, sleep, workouts, cycles) to Claude Desktop on Windows. Read-only, OAuth 2.0, tokens stored on disk protected by Windows ACLs, zero telemetry.
Note: source code comments and docstrings are intentionally kept in French (security-review notes written by the author).
Prerequisites
- Windows 10/11
- Python 3.10+
- An active Whoop account
- A Whoop Developer application (see below)
1. Create a Whoop Developer app
- Go to https://developer.whoop.com and sign in
- Create a new app
- Redirect URI:
http://localhost:3000/callback(exact, no trailing slash) - Scopes to enable:
read:recoveryread:sleepread:workoutread:cyclesoffline(required to obtain a refresh_token)
- Note your
Client IDandClient Secret
Do not enable read:profile or read:body_measurement: this server does not use them.
2. Installation
# Replace <PATH> with the location where you cloned the repo
cd <PATH>\Whoop-MCP
python -m venv venv
.\venv\Scripts\Activate.ps1
pip install -r requirements.txt
For a reproducible and auditable install (exact pinned versions of the ~30 transitive dependencies), use the lock file instead:
pip install -r requirements-lock.txt
3. Connect to Claude Desktop
Open %APPDATA%\Claude\claude_desktop_config.json (create it if it does not exist) and add the whoop section:
{
"mcpServers": {
"whoop": {
"command": "C:\\PATH\\TO\\Whoop-MCP\\venv\\Scripts\\python.exe",
"args": ["C:\\PATH\\TO\\Whoop-MCP\\src\\server.py"],
"env": {
"WHOOP_CLIENT_ID": "your_client_id",
"WHOOP_CLIENT_SECRET": "your_client_secret"
}
}
}
}
Replace both placeholders with your real values.
Restart Claude Desktop.
4. First run
In Claude Desktop, type for example: "Read my latest Whoop recovery".
On the first call, your browser opens the Whoop authorization page. You authorize the app. The local server on localhost:3000 receives the code, exchanges it for tokens, and saves them to %USERPROFILE%\.whoop-mcp\tokens.json with restrictive Windows ACLs (only your user can read the file).
Subsequent calls use the stored tokens. When the access_token expires, the refresh_token is used automatically.
Exposed tools
| Tool | Whoop endpoint | Data |
| --- | --- | --- |
| get_recovery | GET /developer/v2/recovery | HRV, RHR, recovery score |
| get_sleep | GET /developer/v2/activity/sleep | Stages, performance, duration |
| get_workouts | GET /developer/v2/activity/workout | Strain, average HR, sport |
| get_cycles | GET /developer/v2/cycle | Physiological cycle, strain |
All accept start and end (ISO 8601, optional) and limit (1-25, default 10).
Reset authentication
To force a new authorization (revoked access, changed app, etc.):
del "$env:USERPROFILE\.whoop-mcp\tokens.json"
The next call will restart the browser flow.
Verify token ACLs
icacls "$env:USERPROFILE\.whoop-mcp\tokens.json"
You should see only your Windows account (<YOUR_USER>:(F)) — no BUILTIN\Users, no Everyone.
Verify imports without running the flow
$env:WHOOP_CLIENT_ID="dummy"; $env:WHOOP_CLIENT_SECRET="dummy"
.\venv\Scripts\python.exe -c "import sys; sys.path.insert(0, 'src'); import server; print('ok')"
Security — points to review yourself
- Windows ACLs:
src/token_store.py:_apply_windows_aclrunsicacls /inheritance:r /grant:r <user>:F. After the first auth, verify thaticacls tokens.jsonshows only your user. - CSRF state:
src/oauth.py:authorize_interactivecompares_CallbackHandler.received_state != statebefore the exchange. Verify the comparison is strict and happens before theexchange_codecall. - No secret leaked in logs: grepping
access_token\|refresh_token\|client_secretinsrc/should only return internal manipulations, never an argument toprintor anf"..."log.
Architecture (4 files)
src/
server.py # MCP entrypoint, 4 tools
whoop_client.py # GET with refresh-on-401 and backoff on 429
oauth.py # browser flow + token exchange + refresh
token_store.py # load/save/clear + icacls ACL
No dependency other than mcp and httpx (pinned in requirements.txt). No outbound call outside api.prod.whoop.com.