# Reported issues for whoop

Pod holds 24 of 86 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to [whoop](/mcp/whoop).

## Most discussed

### OAuth fails

I have Whoop developer side variables set up as instructed, and the config .json is also correct. But OAuth throws 'invalid_client' with every attempt to open Claude and connect the MCP. Any ideas?

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/36) · 2026-04-13 · closed · external user · 3 comments

### [LOW] No token shape validation in loadTokens — add lightweight field check

## Source
Security Audit #1 — LOW-1

## Problem
`src/auth/token-store.ts:93-99` — `loadTokens()` does `JSON.parse(raw) as OAuthTokens` without validating the shape. A corrupted or tampered `tokens.json` causes confusing runtime errors far from the source.

## Fix
Add lightweight shape validation before returning:
```typescript
function isValidTokenShape(data: unknown): data is OAuthTokens {
  return typeof data === "object" && data !== null
    && "access_token" in data && typeof (data as Record

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/19) · 2026-04-10 · closed · 2 comments

### [MEDIUM] Callback server binds 0.0.0.0 — bind to 127.0.0.1 only

## Source
Security Audit #1 — MEDIUM-3

## Problem
`src/auth/callback-server.ts:159` — `server.listen(port)` with no host argument binds to `0.0.0.0` (all interfaces) by default. Any device on the local network can send requests to the callback server during the ~2-minute OAuth flow window.

## Impact
An attacker on the same network could race to submit a crafted callback. The CSRF state parameter mitigates this, but defense-in-depth says bind to loopback only.

## Fix
```typescript
server.liste

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/18) · 2026-04-10 · closed · 2 comments

### [MEDIUM] Retry-After header not capped — server can force arbitrary sleep

## Source
Security Audit #1 — MEDIUM-2

## Problem
`src/api/client.ts:97-104` — `parseRetryAfter()` accepts any non-negative number from the `Retry-After` header. A malicious or misconfigured server could return `Retry-After: 999999`, causing the client to sleep for ~11.5 days.

## Impact
Denial of service — the MCP server becomes unresponsive for an arbitrary duration based on a server-controlled header.

## Fix
Cap the Retry-After value:
```typescript
const MAX_RETRY_AFTER_MS = 60_000; // 1 mi

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/17) · 2026-04-10 · closed · 2 comments

### [MEDIUM] No request timeout on API client fetch — add AbortSignal.timeout

## Source
Security Audit #1 — MEDIUM-1

## Problem
`src/api/client.ts:128-135` — `fetch()` has no `AbortSignal` or timeout. A slow or unresponsive WHOOP API permanently hangs the MCP server, which runs inside Claude Desktop's process.

## Impact
Denial of service — a hung WHOOP API (or MITM slow-loris attack) freezes the entire MCP connection requiring a Claude Desktop restart.

## Fix
```typescript
const REQUEST_TIMEOUT_MS = 30_000;

return await fetch(url, {
  method: "GET",
  headers: { ... }

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/16) · 2026-04-10 · closed · 2 comments

### [HIGH] Reflected XSS in OAuth callback error page — HTML-encode message

## Source
Security Audit #1 — HIGH-2

## Problem
`src/auth/callback-server.ts:43-49` — `errorHtml()` injects `error_description` query parameter directly into HTML without encoding:
```typescript
function errorHtml(message: string): string {
  return `...<p>${message}</p>...</html>`;
}
```
The `message` comes from attacker-controlled `url.searchParams.get("error_description")`.

## Proof of Concept
```
http://localhost:3000/callback?error=access_denied&error_description=<script>alert('XSS')</scr

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/15) · 2026-04-10 · closed · 2 comments

### [HIGH] OS command injection in openBrowser — replace exec with spawn

## Source
Security Audit #1 — HIGH-1

## Problem
`src/auth/oauth.ts:193-199` uses `exec()` with string interpolation to launch the browser:
```typescript
exec(`open "${url}"`);
```
A malicious `WHOOP_CLIENT_ID` env var containing shell metacharacters (e.g., `"; rm -rf / #`) would be interpreted by the shell, enabling arbitrary command execution.

## Proof of Concept
```bash
WHOOP_CLIENT_ID='foo"; echo PWNED > /tmp/pwned; echo "' node dist/index.js
```

## Fix
Replace `exec` with `spawn` — no she

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/14) · 2026-04-10 · closed · 2 comments

### Sync MCP server version with package.json instead of hardcoding

## Source
Code Review Checkpoint 2 — Important Issue #2 / Suggestion #7

## Problem
`src/server.ts:122` hardcodes `version: "0.1.0"` in `new McpServer({ name: "whoop-mcp", version: "0.1.0" })`. When `package.json` is bumped to a new version, the MCP server will still report `0.1.0`. MCP clients display this version to users.

## Fix
Pass the version from `index.ts` when wiring up in Task 9, or read from `package.json`:

Option A — Accept as parameter:
```typescript
export function createWhoopSer

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/13) · 2026-04-10 · closed · 2 comments

## Most recent

### Token refresh never runs: parseErrorBody() double-reads the response body, throwing before the 401 branch

## Summary

`parseErrorBody()` reads the response body twice, which throws a `TypeError` **before** the 401 token-refresh branch in `doGet()` can run. The result: once a WHOOP access token expires (~1 hour), every subsequent API call fails permanently, and the only recovery is restarting the MCP process.

There is a second, related defect in the same code path: the refreshed token is never written back to `options.accessToken`, so even after the first bug is fixed, every call repeats the full `4

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/219) · 2026-07-29 · closed · external user · 1 comment

### Optionally collapse the three printed-command setup branches into a lookup

**Source:** Code review checkpoint 14, Suggestion #3

**Problem:** In `src/cli/setup.ts` the `claude-code` / `codex` / `copilot` branches are near-identical (label + generator + `out.write`). A `Record<ClientTarget, () => string>` lookup would remove the duplication.

**Fix:** Optional refactor — map each printed-command target to its generator and emit once. Current explicit form is readable and matches existing style, so this is low priority.

**Priority:** Suggestion — backlog

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/181) · 2026-06-13 · open · 0 comments

### Warn users that printed setup commands contain a plaintext secret

**Source:** Code review checkpoint 14, Suggestion #2

**Problem:** In `src/cli/setup.ts`, the `claude-code`, `codex`, and `copilot` emission branches print a shell command that embeds `WHOOP_CLIENT_SECRET` in cleartext. Pasting it records the secret in shell history. Consistent with the pre-existing claude-code path (not a regression), but worth a one-line warning.

**Fix:** Add a note alongside each printed command, e.g.:
```ts
out.write("Note: this command contains your client secret — your sh

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/180) · 2026-06-13 · open · 0 comments

### Strengthen Copilot setup escaping test with full shell+JSON round-trip

**Source:** Code review checkpoint 14, Suggestion #1

**Problem:** `tests/cli/setup.test.ts:159-167` only asserts the generated `code --add-mcp` command `toContain('\'')`. The Copilot path embeds creds via `JSON.stringify` then shell-quotes the whole payload, so the strongest proof is a round-trip: unwrap the shell single-quoting, `JSON.parse`, and assert the apostrophe-containing secret survives both layers intact.

**Fix:**
```ts
const inner = cmd.slice(cmd.indexOf("'") + 1, cmd.lastIndexOf("'

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/179) · 2026-06-13 · open · 0 comments

### Document getOrFetch undefined-value conflation assumption

**Source:** code-review-checkpoint-13, Nit #1

**Problem:** `src/cache/memory-cache.ts:148-151` — `get()` returns `undefined` for both an absent key and a stored `undefined` value, so a fetcher that legitimately resolves to `undefined` would never cache-hit. Harmless today (WHOOP responses are always objects).

**Fix:** Add a one-line comment noting the assumption so the cache is not silently reused for nullable values later.

**Priority:** Nit (backlog)

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/177) · 2026-06-13 · open · 0 comments

### Remove or wire up the dead MemoryCache.invalidateAll() alias

**Source:** code-review-checkpoint-13, Minor #3

**Problem:** `src/cache/memory-cache.ts:131-133` — `invalidateAll()` is an alias for `clear()` carried over from the removed `ResourceCache` API, but nothing in `src/` calls it (the token-refresh site in `src/index.ts` calls `cache.clear()` directly). It survives only via its own unit test.

**Fix:** Either remove the alias (and its test) to shrink the public surface, or point the token-refresh call site at `invalidateAll()` so the alias earns its

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/176) · 2026-06-13 · open · 0 comments

### MemoryCache.clear() does not purge in-flight map; post-clear reader can receive pre-clear data

**Source:** code-review-checkpoint-13, Minor #1

**Problem:** `src/cache/memory-cache.ts` — `clear()` empties `store` and bumps `generation` but leaves the `inflight` map intact. A `getOrFetch(key)` issued after `clear()` but before the pre-clear fetch settles hits the still-present in-flight entry and resolves with the pre-clear value (returned once, not cached thanks to the generation guard). In the token-refresh path a request arriving right after `cache.clear()` can be served data fetched mi

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/174) · 2026-06-13 · open · 0 comments

### [LOW] MemoryCache.clear() does not drop the in-flight map

**Source:** Security Audit Report #9 — finding LOW-2 (`docs/security-audits/security-audit-9.md`)

**Problem:** `MemoryCache.clear()` empties `store` and bumps `generation` but leaves the `inflight` map intact (`src/cache/memory-cache.ts:127-130`). If a token refresh (`cache.clear()` in `onTokenRefresh`, `src/index.ts:138`) happens while a `getOrFetch` for key K is still in flight, a later caller for K joins the **pre-refresh** in-flight promise (`src/cache/memory-cache.ts:145-170`). Because the

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/173) · 2026-06-13 · open · 0 comments

### [LOW] Path-only cache keying assumes a single WHOOP identity

**Source:** Security Audit Report #9 — finding LOW-1 (`docs/security-audits/security-audit-9.md`)

**Problem:** The shared in-memory cache key is derived solely from the request path and sorted query params — `GET:${base}?${sortedParams}` (`src/api/client.ts:139-152`, `cacheKey`). A single process-wide `MemoryCache` is used (`src/index.ts:124`). This is correct for the current single-WHOOP-token design, but the server can run in `http`/`both` transport mode where multiple MCP clients connect (al

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/172) · 2026-06-13 · open · 0 comments

### Add end-to-end MCP tool integration test over HTTP transport

**Source:** Code Review Checkpoint 12, Suggestion #2

**Problem:** The HTTP transport tests (`tests/transport/http.test.ts`) verify auth, CORS, limits, and routing, but don't test that an actual MCP `initialize` → `tools/call` sequence works end-to-end over HTTP. Acceptance criteria include 'All 14 tools work identically over HTTP.'

**Fix:** Add an integration test in Task 13g (full verification) that connects an MCP client to the HTTP server, initializes a session, and calls at least one tool.

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/161) · 2026-06-03 · closed · 0 comments

### trustProxy _realIp stored but never used — dead code in HTTP transport

**Source:** Code Review Checkpoint 12, Suggestion #1

**Problem:** In `src/transport/http.ts:184-187`, the `trustProxy` option extracts the client IP from `X-Forwarded-For` into `req._realIp` but it's never read anywhere. This is dead code currently.

**Fix:** Either add a comment noting this is prep for Task 13b (structured logging with request IP), or defer the implementation entirely to 13b when the logger can consume it.

**Priority:** Suggestion — backlog

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/160) · 2026-06-03 · closed · 1 comment

### Fix double decrement of activeConnections on body parse failure in HTTP transport

**Source:** Code Review Checkpoint 12, Issue #1

**Problem:** In `src/transport/http.ts:237-244`, when JSON body parsing fails, the code explicitly decrements `activeConnections` AND the previously-registered `res.on("close")` handler also decrements it when the response ends. This double-decrement drives the counter negative over time, effectively disabling connection limiting after enough malformed requests.

**Fix:** Remove the explicit `activeConnections--` in the catch block. The `res.on("c

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/159) · 2026-06-03 · closed · 0 comments

### Add test coverage for get_calendar with start parameter

**Source:** Checkpoint 11, Suggestion #2

**Problem:** `tests/tools/get-calendar.test.ts` tests `days` and default behavior but never exercises the `start` parameter path. The semantic issue in the start parameter (Issue #154) would have been caught with explicit tests.

**Fix:** Add tests:
- `it('uses start parameter to determine grid origin')`
- `it('handles start + days interaction correctly')`
- `it('handles start date in the future gracefully')`

**Priority:** Suggestion — v0.4.1

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/157) · 2026-05-31 · closed · 0 comments

### get_calendar workout_count field is always 0 (dead placeholder)

**Source:** Checkpoint 11, Suggestion #1

**Problem:** In `src/tools/get-calendar.ts:178`, `workout_count` is hard-coded to 0 with comment "Workout count not available from cycle endpoint directly." This returns misleading data to consumers — hard-coded zeros look like real data.

**Fix:** Either remove the `workout_count` field from `CalendarDay` interface until it can be implemented, or fetch the workout endpoint to populate it.

**Priority:** Suggestion — backlog

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/156) · 2026-05-31 · closed · 0 comments

### get_today returns empty snapshot instead of throwing when all primaries fail

**Source:** Checkpoint 11, Important Issue #2

**Problem:** In `src/tools/get-today.ts:128-132`, the throw condition requires BOTH all primaries AND workout to fail. If recovery+sleep+cycle all fail but workout succeeds, the function returns `{recovery: null, sleep: null, strain: null, summary: "No data available yet today"}`. This is useless because workout data only surfaces inside the strain block (which requires cycle). Violates the spec: "If ALL endpoints fail, throws error (not partial emp

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/155) · 2026-05-31 · closed · 1 comment

### [LOW] compare_periods: Add ISO 8601 regex validation to date input schema

Source: Security Audit 7, Finding LOW-2

Problem: compare_periods tool in src/server.ts:345-352 uses bare z.string() for date parameters without regex validation. While the handler correctly rejects invalid dates via validateDateRange(), the error messages for completely malformed input may confuse LLM clients.

Impact: No security vulnerability. Input is safely rejected at the validation boundary. Improvement to schema strictness and error clarity.

Fix: Add .regex() to the Zod schema for all f

[Read the thread](https://github.com/shashankswe2020-ux/whoop-mcp/issues/153) · 2026-05-31 · closed · 0 comments

The remaining reports are on [the project's issue tracker](https://github.com/shashankswe2020-ux/whoop-mcp/issues).
