cadence
This server gives an AI local access to your personal physiological data from WHOOP and Apple Health, enabling health-aware interactions. Here's what you can do:
Get a holistic health summary (
health_context): Retrieve a natural-language overview of your recent physiological state — last night's sleep, current recovery score (or raw vitals if unavailable), most recent workout, and step-count trend.Query sleep sessions (
get_sleep): Fetch detailed sleep records from the last N days from WHOOP and/or Apple Health, with support for deduplication (WHOOP wins same-night conflicts by default) or raw multi-source output.Query WHOOP recovery data (
get_recovery): Pull WHOOP-specific recovery records including recovery score, HRV/RMSSD, resting heart rate, SpO2, and skin temperature.Query workout records (
get_workouts): Retrieve workout history across both WHOOP (strain and heart-rate data) and Apple Health.Query a daily metric time series (
get_daily_metric): Get a day-by-day series for a specific metric — step count, active/basal energy, resting heart rate, HRV (SDNN), respiratory rate, VO2max, exercise minutes, or walking/running distance.Analyze metric trends (
get_trends): Compare a metric's recent average against a prior equivalent period (e.g., this week vs. last week), returning recent average, prior average, and percent change.Discover body-code correlations: Analyze correlations between your physiological data and coding activity (e.g., git commit-hour rhythm).
Integrates with Apple Health to import local health data (steps, energy, exercise minutes, distance, HRV, etc.) via a one-time export zip file, providing local health context without network calls.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@cadencehow was my sleep last night?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
cadence
A local-first health-context engine. Pulls WHOOP and Apple Health into one local store, and gives Claude real, current knowledge of how you're actually doing -- sleep, recovery, strain, workouts -- instead of working blind.

The honest privacy story
Unlike the rest of this portfolio (lantern, stacks, orchard), cadence can't claim "nothing leaves the machine" -- WHOOP data necessarily comes from WHOOP's own cloud API over OAuth, there's no way around that. What's real and true instead: once pulled, your health data lives only on this machine (a local SQLite file at ~/.cadence/cadence.db, permissioned 0600, owner-only), your OAuth tokens live in the macOS Keychain (never on disk in plaintext, never in this repo), and cadence never re-uploads or re-shares anything to a third party. Apple Health data never leaves the machine at all -- it's a local file import, no network call involved. Claude reads your data locally via MCP; nothing about that transmits it anywhere else either.
Related MCP server: WHOOP MCP Server
Install
pip install -e .Apple Health (no setup, fully local):
cadence import-apple-health ~/Downloads/export.zip
cadence brief(Export from iPhone: Health app -> profile icon -> Export All Health Data.)
WHOOP (one-time OAuth setup, see below):
cadence auth # opens a browser once, stores tokens in Keychain
cadence sync # pulls the last 30 days of recovery/sleep/workouts
cadence briefMCP server (so Claude can query this live instead of just reading the brief):
cadence mcpPoint your Claude client at it (claude mcp add cadence -- cadence mcp, or the equivalent stdio-server config for your client).
WHOOP setup (one-time)
Register a personal app at developer.whoop.com (sign in with your normal WHOOP account, create a Team if prompted, then create an App). Fill in:
Privacy Policy: a real link is required --
https://github.com/<your-fork>/cadence#the-honest-privacy-story(this section) is a reasonable choice for a personal-use app.Redirect URL: must exactly match
cadence.auth.REDIRECT_URI(https://github.com/rajanshxrma/cadenceby default). WHOOP's own form only acceptshttps://or a custom URL scheme -- a plainhttp://localhostredirect is rejected, which is whycadence authuses a copy-paste flow instead of a local callback server (seeauth.py's module docstring).Scopes: check all six data scopes (
read:recovery,read:cycles,read:sleep,read:workout,read:profile,read:body_measurement). There's noofflinecheckbox on the form -- that scope is requested dynamically in the actual authorization request, not a per-app setting.Webhooks: leave empty -- cadence is pull-based (
cadence sync), no webhook receiver.Apps work immediately for up to 10 WHOOP accounts in dev status -- no approval needed for personal use.
Export your credentials (shown once on the app's page after creation -- copy both immediately):
export CADENCE_WHOOP_CLIENT_ID="..." export CADENCE_WHOOP_CLIENT_SECRET="..."Run
cadence auth. It opens your browser; after you approve, WHOOP redirects there with a code in the URL -- copy the full address-bar URL and paste it back into the terminal when prompted. Tokens land in Keychain.cadence syncafter that just works -- refresh happens automatically (WHOOP rotates refresh tokens on every use; cadence persists the new one each time, per their API's requirement).
Client id/secret are read from environment variables only -- never written to disk, never committed. keyring (the library used for token storage) needs an OS credential store; on macOS that's Keychain, no extra setup.
Health-aware AI sessions (v0.2.0)
The piece that makes this more than a data viewer: cadence hook emits a Claude Code SessionStart hook payload, so the first Claude session of each day opens already knowing your physiological state -- last night's sleep, today's recovery, recent strain -- plus a one-line adaptive hint when the numbers warrant one (low recovery: "favor lighter work over marathon builds"; short sleep: flagged; data gone stale: a re-sync nudge instead of silent death). Second session of the day: silent, by design -- a hook that talks every session is noise.
cadence hook # prints the SessionStart JSON (or nothing -- silence is a feature)
cadence sync --quiet # unattended mode for a launchd/cron job; macOS-notifies if re-auth is ever needed
cadence auth --store-client # one-time: persist client creds to Keychain so unattended sync needs no env varsWire-up: a 15-line shim script in your hooks config calls cadence hook; a launchd job runs cadence sync --quiet twice daily. The hook never blocks a session -- no network calls (local SQLite reads only), and every failure path chooses silence over a broken session startup.
Personal baselines and anomaly detection (v0.3.0)
Every threshold below is PERSONAL, never a population norm -- cadence builds a real 30-day rolling mean/std of your own history per metric (recovery score, HRV, resting heart rate, respiratory rate, sleep duration/efficiency) and flags today's value only when it's a real statistical deviation (|z| >= 1.5) from your normal, not some generic chart's idea of normal.
The honesty guardrail that matters most here: a metric is silently omitted from baselines (not shown with a misleading small n) until there are 14+ days of real history behind it. An anomaly built on 3 data points is noise dressed up as insight -- this project would rather say nothing than say something confidently wrong.
cadence baseline # prints current baselines + any deviations todayAnomalies surface three ways, each calibrated to how urgent they are:
In
brief/the session hook -- inline, every time they exist.A focused re-alert mid-day -- if a NEW serious deviation (|z| >= 2.5) appears after the day's first session already happened (e.g. an 8pm sync lands something the 8am one didn't have), the hook fires again just for that -- a real new signal outranks the once-daily quota. Already-seen deviations never re-fire the same day.
A macOS notification from the unattended sync job for the serious tier specifically -- the one thing worth interrupting silence for.
Weekly digest: cadence digest writes a dated summary to a configurable directory and can optionally commit it to a notes/journal repo. Email delivery is intentionally not wired up -- see digest.py's module docstring for the reasoning.
The mirror: body, code, and AI usage in one place (v0.4.0)
This is the piece that doesn't exist anywhere else. A few WHOOP-MCP servers exist publicly; a health tool alone isn't novel. What's actually new: cadence is the first place your git commit history, your Claude usage, and your physiological data live in one local correlation layer -- all three time-series were already sitting on the same machine, just never joined.
cadence config add-repo ~/Downloads/your-project # point it at your own repos -- fully generic
cadence mirror # commit-hour rhythm + observational correlationsThe honesty discipline that makes this real rather than a toy: every correlation is a simple two-group mean comparison on YOUR OWN history -- no regression, no significance testing dressed up as more than it is -- and it refuses to report anything with fewer than 14 days of real overlapping data per group, full stop. Every result that does print carries an explicit methodology note (exactly how the two variables were date-joined, stated plainly as "observational, not causal"). Silence is the honest answer below that threshold, never a confident-sounding number built on noise.
The honesty guardrail in practice: the commit-hour histogram is available immediately (a pure histogram has no threshold to clear), but the deeper correlations (late-night coding vs. next-morning recovery, short sleep vs. commit output, heavy Claude usage vs. next-day recovery) return nothing until there are 14+ days of real overlapping data per group -- on a fresh setup they correctly report nothing rather than manufacture a "finding" from 2-3 data points, and start reporting real numbers only as the logs accumulate past that threshold.
MCP tools: get_developer_rhythm(), get_body_code_correlations().
On-device narration (v0.5.0)
cadence digest --narrate turns the structured weekly digest into 2-3 plain-spoken paragraphs, generated entirely on-device via langchain-apple-foundation-models. Zero cloud, zero cost, pip install cadence[narrate] (optional -- cadence never depends on Apple Intelligence to work).
cadence digest --narrate --no-commit # try it without committing anythingA real hallucination, found and fixed live, not glossed over: the first working prompt produced a real run where the digest's own baseline section plainly showed a fully-populated recovery baseline (well past the n >= 14 threshold) and the model still wrote "there isn't enough data to compare your recovery score to your 30-day baseline" -- flatly contradicting its own input. Same bug class already documented in lantern's README. Fixed by making the instructions explicit about what "not enough data" is allowed to mean (only when that literal phrase appears in the input); verified across 3 consecutive real generations post-fix with zero hallucinated claims and every number reproduced verbatim. Not proof it can never recur -- prompting alone never fully eliminates hallucination -- which is exactly why the narration is inserted ABOVE the deterministic structured digest, never replacing it: the real numbers are always sitting right there to cross-check against.
How it works
WHOOP | Apple Health | |
Where the data comes from | Cloud API, OAuth 2.0 | Local |
What it uniquely has | Recovery score, HRV (RMSSD), strain, sleep performance/efficiency | Steps, active/basal energy, exercise minutes, distance, HRV (SDNN), workout GPS-adjacent detail |
Network calls | Yes, to pull data ( | None -- pure local file parse |
Sync model | Pull-based, run | One-time import per export (re-import to refresh) |
Both sources get normalized into one local SQLite schema (sleep, recovery, workouts, daily_metrics), each row tagged with its source. When both sources cover the same real-world event (e.g. the same night's sleep), cadence doesn't silently merge them into a fake "canonical" record -- it picks explicitly and says so: WHOOP wins for sleep staging and is the only source for recovery/strain (Apple Health has no equivalent computed score); Apple Health is the source for step count and other daily activity WHOOP's API doesn't expose.
Real engineering challenges (found and handled, not glossed over)
The Apple Health export can be 200MB-1GB+ of XML.
apple_health.pystreams it withxml.etree.ElementTree.iterparseand actively evicts every processed element from the parse tree --elem.clear()alone isn't enough (a well-known stdlib gotcha: the element still sits in its parent's children list). Verified, not just claimed: a 300k-record/48MB synthetic file grew peak RSS by ~19MB with full eviction vs ~27MB withelem.clear()alone -- a real, measurable difference that gets far more pronounced at real-export scale (millions of records).Apple Health emits sleep as dozens of small per-stage records, not one row per night.
_group_sleep_sessions()reduces them into real sessions by contiguous time (a >2h gap starts a new night), summing per-stage seconds -- tested against both a gap-split case and a same-night merge case.WHOOP rotates refresh tokens on every use -- the old one is invalidated the instant a new one is issued.
auth.py's refresh path persists the new refresh token immediately, before returning; dropping that step "to simplify" would break sync after the very next token rotation.A real off-by-one bug in the trend calculation, caught by its own test: comparing "this week" vs "last week" with naive inclusive date bounds put the shared boundary day in both windows, inflating the recent average (measured: 8625 instead of the correct 9000 in a 9000-vs-6000 synthetic scenario). Fixed by making the two windows properly disjoint.
A real test-isolation bug, caught by its own test:
store.connect()'s default argument originally boundDEFAULT_DB_PATHat function-definition time, so reassigningstore.DEFAULT_DB_PATHin a test (to isolate it from the real~/.cadence/cadence.db) silently had no effect -- an early test run actually wrote real synthetic test rows into the real local store on the dev machine before this was caught and fixed.tests/test_tools.py::test_tools_never_touch_real_default_db_pathnow guards against a regression.A partial-day bias in the trend math, found by the first run against real data -- the kind of bug synthetic tests can't catch. The week-over-week trend originally included today in the recent window; a
cadence briefrun in the early morning counted that morning's few hundred steps as a full day, and the first live report claimed steps were "down 56% vs the week before" -- partly an artifact of when the command happened to run, not real behavior. A health brief that's subtly wrong depending on the time of day is worse than no brief. Both comparison windows now cover complete days only, ending at yesterday, with a regression test pinning today's exclusion.WHOOP's
start/endparams silently 404 on a bare date. Found live:?start=2026-06-10returns a plain 404 (not a validation error), while the identical request with?start=2026-06-10T00:00:00.000Zreturns 200 with data. Full ISO8601 datetimes only -- now enforced by_iso_datetime()with its own regression test.The same lexicographic-date-comparison bug, twice, in two different modules.
sleep/workoutsrows store full ISO timestamps ("2026-07-10T03:00:00Z"), which sort after a bare date string ("2026-07-10") -- so a baredate.today().isoformat()upper bound silently excludes today's own rows. This was already found and fixed once inapple_health.py's tests; it recurred indigest.py(a genuinely new bug, not a regression of the first) because the fix pattern (brief.py's+1 daybuffer) wasn't applied to the new module by default. Now consistent everywhere query bounds are built. Worth remembering: a fixed bug in one module doesn't protect a sibling module written later -- the pattern has to be applied deliberately each time, not assumed.A real correlation bug, caught by its own test before it ever touched real data.
late_night_cost()originally iterated over commit-days to build its two comparison groups -- which meant days with ZERO commits (exactly the "no late-night commit" group it needed) were silently excluded entirely, since they never appear in commit data at all. Fixed by iterating over health-outcome days instead and defaulting missing commit data to zero, the same patternsleep_to_output()already used correctly. Caught by a real end-to-end test (a synthetic repo + synthetic recovery data) before this ever ran against a real account -- had it shipped as written, real correlation results would have been silently computed from a biased, commits-only subset instead of the true comparison.
Limitations, stated honestly
WHOOP field mapping is live-verified against a real account (a real 30-day sync against a live account pulled a full set of recovery, sleep, and workout records, and the brief rendered them correctly). Two real integration bugs surfaced during that first live run and are fixed with regression tests -- see challenges #6 and #7 above.
Apple Health parsing covers a curated allowlist of metric types (steps, energy, distance, exercise minutes, resting heart rate, HRV-SDNN, respiratory rate, VO2max) and the classic attribute-based
<Workout>schema -- not every HealthKit type Apple can export, and units are assumed to be Apple's standard emitted units except where explicitly converted.Apple Health workouts have no stable id in the export format -- cadence synthesizes one from (start time, activity type), an extremely-low-collision approximation, not a real Apple-issued identifier.
Apple Health's own HRV measure (SDNN) and WHOOP's (RMSSD) are genuinely different statistics -- cadence keeps them in separate fields rather than pretending they're interchangeable.
Development
pip install -e ".[dev]"
pytest -vReal, no-mock tests throughout: a synthetic (non-personal) Apple Health export fixture exercised end-to-end, WHOOP normalization against synthetic-but-realistically-shaped JSON (no live token needed), and every MCP tool invoked through the actual mcp protocol layer, not just imported and called directly.
MIT.
Available Tools
6 toolsget_daily_metricA
Returns a daily time series for one metric (e.g. "step_count",
"active_energy_kcal", "resting_heart_rate_bpm", "hrv_sdnn_ms",
"respiratory_rate_bpm", "vo2max", "exercise_minutes",
"walking_running_distance_m", "basal_energy_kcal") over the last
days days, oldest first. All of these come from Apple Health except
where WHOOP populates the same concept via get_recovery().
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| metric | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. It explains the return format (time series, oldest first) and data source, but does not explicitly state side effects (e.g., read-only) or authentication requirements. It adds some value beyond the tool name but lacks complete behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the main action and examples, and no redundant information. Every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (2 parameters, output schema exists), the description covers the purpose, parameters, data source, and ordering. It is sufficiently complete for an agent to use correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so description must compensate. It explains the `days` parameter (number of days) and lists example valid values for `metric`, adding significant meaning beyond the schema. However, it does not specify constraints like integer range or case sensitivity for metric.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it returns a daily time series for a specific metric, lists examples, and distinguishes from sibling tool get_recovery by explaining the source overlap. It is specific and actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides context for when to use this tool (for Apple Health metrics) and hints at alternatives (get_recovery for WHOOP-populated metrics), but does not explicitly state when not to use it or cover all siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recoveryA
Returns raw WHOOP recovery records (recovery score, HRV, resting
heart rate, SpO2, skin temp) from the last days days, newest first.
WHOOP-only -- Apple Health has no equivalent computed recovery score.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that results are ordered newest first and lists the data fields. However, it does not explicitly state that the tool is read-only or safe, nor does it mention any potential side effects or limitations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no wasted words. The first sentence delivers the core functionality, the second adds crucial context. Every part earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has an output schema (expected), the description does not need to detail return values. It sufficiently covers the source, metrics, time range, ordering, and exclusivity. Minor gap: no mention of pagination or result limits, but acceptable for a simple retrieval tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has one parameter 'days' with 0% schema description coverage. The description adds meaning by specifying that it retrieves data 'from the last `days` days', clarifying the semantics beyond the schema's type and default.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Returns' and the resource 'WHOOP recovery records', listing specific metrics. It also distinguishes from siblings by noting 'WHOOP-only', differentiating from Apple Health and implying no equivalent in other tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for retrieving recent recovery data ('from the last `days` days, newest first') and specifies WHOOP-only, hinting at when not to use (Apple Health users). However, it does not explicitly mention when to use this tool versus its siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_sleepA
Returns sleep session records from the last days days, newest
first, across both WHOOP and Apple Health (each row's source field
says which). By default, nights covered by both sources return only
the WHOOP row (it wins the same-night precedence rule) so durations
can be summed without double-counting; pass dedupe=False to get every
raw row from both sources. Use this for sleep detail beyond what
health_context() summarizes -- e.g. a specific night's stage breakdown.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| dedupe | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. Discloses deduplication logic, source field, precedence rule, and ordering. Implies read-only nature but does not explicitly state it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single paragraph with logical flow: main purpose, deduplication details, usage guidance. Front-loaded and efficient, though slightly lengthy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given parameters (2, no required, no enums) and presence of output schema, description adequately explains tool purpose, parameters, and behavior relative to siblings. Could mention which fields are in output but output schema covers that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, but description adds significant meaning: `days` explained as 'last `days` days' with default, and `dedupe` explained in detail including default behavior and same-night precedence.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states returns sleep session records from last `days` days, newest first, from two sources. Distinguishes from sibling `health_context` by saying 'Use this for sleep detail beyond what health_context() summarizes'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use (for sleep detail beyond summary) and explains dedupe behavior with same-night precedence. Implicitly suggests not to use when only summary is needed, but lacks explicit when-not-to-use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_trendsA
Compares the average of metric (see get_daily_metric for valid
names) over the last window_days days against the window_days
before that -- e.g. window_days=7 answers "is this week's step count
up or down vs last week." Returns recent_avg, prior_avg, and
delta_percent (None if there's no prior-window data to compare against).
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes | ||
| window_days | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses the algorithm (compare averages over two windows) and return fields (recent_avg, prior_avg, delta_percent) with a note on None for missing prior data. However, no annotations provided, and the description does not cover potential edge cases like non-numeric metrics or timezone handling.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences front-loaded with purpose, followed by example and return format. No extraneous words, logical flow.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers key return fields without output schema, references sibling tool for valid metrics, and gives example. Does not explicitly state that metric must be numerical or handle errors, but adequately addresses core functionality.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema has 0% coverage, but the description explains both parameters: metric references get_daily_metric for valid names, window_days explained with example. Sufficiently compensates for missing schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the tool compares averages over two windows, specifies the metric reference from get_daily_metric, and provides a concrete example. Differentiates from sibling tools by focusing on trend analysis vs raw daily values.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Implicitly suggests use for period-over-period comparison but does not explicitly state when to use versus alternatives like get_daily_metric. The example aids understanding but lacks clear when-not guidelines.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_workoutsA
Returns workout records from the last days days, newest first,
across both sources. WHOOP rows include strain/heart-rate; Apple
Health rows include whatever the Health app logged for that workout.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It discloses ordering, time range, and source-specific data. However, it omits details like data freshness, authentication needs, rate limits, or behavior for invalid days values.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the main action and key details. No unnecessary words or redundancy. It efficiently conveys purpose, scope, ordering, and data sources.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has one optional parameter and an output schema, so the description need not explain return values. It covers purpose, scope, ordering, and source specifics. Minor lack: whether there is a maximum `days` value or behavior for 0 days.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 0% description coverage, but the description explains the `days` parameter (range, default 7, meaning 'last N days'). This adds value beyond the schema's type and default. No other parameters exist, so this is sufficient.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that the tool returns workout records from the last `days` days, ordered newest first, from two sources (WHOOP and Apple Health), with specific data included from each. This distinguishes it from sibling tools like get_daily_metric or get_sleep.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for retrieving recent workouts but does not explicitly state when to use this tool versus alternatives like health_context or get_recovery. No when-not-to-use or prerequisite information is provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
health_contextA
Returns a natural-language summary of recent physiological state: last sleep, current recovery (or raw vitals if no WHOOP recovery score is available), the most recent workout, and a step-count trend. This is the tool to call for "how is rajan doing" -- everything else below is for a more specific follow-up question.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses the tool returns a natural-language summary, includes conditional logic (raw vitals if no WHOOP recovery), and lists components. Does not discuss auth or rate limits, but for a read-only summary tool, this is sufficient. Lacks potential error or limitation details, but overall transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two efficient sentences: first states purpose and contents, second gives usage guidance. No wasted words, front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given zero parameters, existence of output schema, and sibling tools for specifics, the description is complete. It covers purpose, contents, and when to use. Output format is presumably in the output schema, so not required here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Tool has zero parameters, and schema coverage is 100%. Baseline is 4 as per guidelines. Description does not need to add parameter info.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it returns a natural-language summary of recent physiological state, listing contents like sleep, recovery, workout, step trend. It also distinguishes itself from sibling tools by specifying it is for 'how is rajan doing' while siblings are for specific follow-ups.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance: 'This is the tool to call for "how is rajan doing" -- everything else below is for a more specific follow-up question.' Clearly tells when to use and implies when to use alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.1.0- First observed
get_daily_metric - First observed
get_recovery - First observed
get_sleep - First observed
get_trends - First observed
get_workouts - First observed
health_context
TDQS
Scored across 6 tools
Each tool targets a distinct aspect of health data: daily metric time series, WHOOP recovery, sleep sessions, trend comparison, workouts, and a natural-language summary. Descriptions clearly differentiate overlapping metrics by source and form (e.g., resting heart rate in get_daily_metric vs. in get_recovery).
All six tools follow a consistent get_<noun> pattern in snake_case. No mixing of styles or verbs, making the tool surface predictable.
Six tools is well-scoped for a health data aggregation server: one for daily metrics, recovery, sleep, trends, workouts, and a summary. Each tool serves a clear purpose without redundancy.
The tool set covers the main areas of personal health data (daily metrics, recovery, sleep, workouts, trends, summary). A minor gap is the lack of intraday or raw sensor data, but given the focus on daily aggregates and summaries, the coverage is strong.
Maintenance
Related MCP Connectors
WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.
- SomviaOAuthapp.somvia
Private Apple Health metrics and workout detail for ChatGPT, Claude, and any MCP client.
Connect Claude to your Intervals.icu watch data for fitness, workout review, and plan writing.
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceConnects WHOOP fitness data to Claude Desktop, enabling natural language queries about workouts, recovery, sleep patterns, and physiological cycles with secure OAuth authentication and local data storage.458 npm27MIT
- -licenseNot gradedqualityNot gradedmaintenanceConnects WHOOP fitness data to Claude Desktop, enabling natural language queries about workouts, recovery, sleep patterns, and health metrics while keeping data secure and private.-
- AlicenseAqualityDmaintenanceGives Claude access to your WHOOP health data including recovery, sleep, workouts, cycles, body measurements, and profile via the WHOOP Developer API.714 npmMIT
- AlicenseNot gradedqualityDmaintenanceExposes Whoop fitness data (recovery, sleep, strain, workouts) to Claude for use as a daily training coach, enabling natural language queries about your health metrics and training readiness.MIT