vitals-mcp
Reads Android Health Connect export zips to ingest heart rate, sleep, SpO2, steps, and other health data for local analysis.
Works with Fitbit data written to Android Health Connect, enabling ingestion and analysis of heart rate, sleep, and SpO2 metrics.
Works with Garmin Connect data written to Android Health Connect, enabling ingestion and analysis of heart rate, sleep, and SpO2 metrics.
Syncs Health Connect export zips from a Google Drive folder via rclone, pulling the newest export before ingestion and analysis.
Ingests Google Takeout Fit JSON data to incorporate heart rate, sleep, steps, and other fitness metrics into the local database.
Works with Samsung Health data written to Android Health Connect, enabling ingestion and analysis of heart rate, sleep, and SpO2 metrics.
Provides read-only SQL querying, schema access, and metric retrieval from a local SQLite database of computed health data.
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., "@vitals-mcpWhat was my recovery score and 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.
wannabe-fit
Whoop-style recovery, strain, and sleep metrics from a cheap watch, computed on your own machine from raw Android Health Connect exports. No cloud account, no subscription, no dashboard, no AI inference. The output is a SQLite database, CSV dumps, Markdown reports, and an MCP server so that Claude Code, OpenClaw, or any other agent can query your data.
Built for a CMF Watch Pro 2 that syncs through the Nothing X app. It works with any watch or app that writes heart rate, sleep, and SpO2 into Health Connect: Samsung Health, Garmin Connect, Zepp, Fitbit, Oura, Polar, Coros, and most budget wearables.
flowchart LR
W[Watch] -->|Bluetooth| APP[Vendor app]
APP -->|writes samples| HC[Android Health Connect]
HC -->|scheduled export zip| GD[Google Drive folder]
GD -->|rclone copy or manual drop| IN[data/inbox]
IN -->|vitals ingest| DB[(data/vitals.db)]
TK[Google Takeout Fit JSON] -->|vitals ingest| DB
DB -->|vitals compute| M[daily and workout_metrics tables]
M -->|vitals report| R[data/reports/latest.md]
M -->|vitals-mcp| C[Claude Code / OpenClaw / any MCP client]
M -->|vitals sql / export| CSV[CSV]Why this exists
Watches in the 50 to 100 dollar range collect the same raw signals as a Whoop band: continuous heart rate, sleep stages, SpO2, steps. What they lack is the analysis layer, and their apps keep the raw samples locked in. Health Connect changes that. Since Android 15 the phone can export its whole Health Connect database as a zip on a daily schedule. Inside is one unencrypted SQLite file with every sample every app ever wrote.
This tool reads that file and applies published sports-science formulas to it: Banister TRIMP, Karvonen zones, the impulse-response fitness model, Keytel calorie estimation, sleep debt, and a recovery score built from resting heart rate, sleep, and SpO2. Every number is reproducible from the raw tables, every constant is in one file, and every formula is documented below.
The Google Fit REST API is not an option any more. Google closed it to new sign-ups in May 2024 and ends it at the end of 2026. Health Connect exports are the durable path.
Related MCP server: catence
What you get
A daily table with one row per local day:
Group | Columns |
Recovery |
|
Sleep |
|
Load |
|
Body |
|
Activity |
|
Quality |
|
A workout_metrics table with one row per session: duration, average and peak HR, percent of HR
max, zone minutes, TRIMP, strain, HR-derived kcal next to the device kcal, and 1-minute heart-rate
recovery. Plus the raw tables underneath, so you can compute anything else yourself.
Setup
Requirements: Python 3.12+, uv, an Android phone on Android 15 or newer, and a watch whose app writes to Health Connect.
git clone https://github.com/devsrijit/wannabe-fit.git
cd wannabe-fit
uv sync
cp config.example.yaml config.yaml # then edit: sex, birth year, weight, timezoneOn the phone:
In the watch app, turn on 24/7 heart rate, SpO2, and sleep tracking. Pick the shortest heart-rate interval it offers. Make sure the app is allowed to write to Health Connect.
Settings > Security and privacy > Health Connect > Manage data > Export. Set a daily schedule and pick a Google Drive folder.
Get the zip to your computer. Either:
Install rclone, run
rclone config create gdrive drive scope=drive.readonly, and setrclone.remote_path: "gdrive:<your folder>"inconfig.yaml.vitals syncthen pulls the newest zip before every run. rclone's shared Google client is being retired during 2026, so create your own OAuth client ID as described in the rclone docs.Or drop the zip into
data/inbox/by any means. Any file name works.
Then:
uv run vitals doctor # what the export contains, sample intervals, sources
uv run vitals sync # pull, ingest, compute, write data/reports/latest.md
uv run vitals today # the day cardIf doctor shows a table the watch should fill but did not, the fix is in the watch app's settings.
Run it in the background (macOS)
scripts/install-launchd.sh # every 6 hours
scripts/install-launchd.sh 3600 # every hourThis installs a launchd agent named com.wannabe-fit.sync that runs vitals sync and logs to
data/sync.log. The sync is idempotent: re-ingesting the same export changes nothing, and rclone only
transfers the zip when it changed. On Linux, a cron line running uv run --project <path> vitals sync
does the same job.
Web dashboard
web/ is a Next.js site over the same database: recovery ring, hypnogram, zone and load charts, per-workout heart-rate traces, and a glossary that explains every metric with its formula and source. See web/README.md.
cd web && bun install && bun run devInterfaces
Command line
Command | What it does |
| Inventory of an export and of the local db, sample gaps, last sync |
| Load a Health Connect zip or db, or a Takeout Fit zip or folder. Default: newest file in |
| Recompute |
| rclone pull (if configured) + ingest + compute + report |
| The latest day card |
| Markdown report with daily and workout tables |
| Read-only SQL, CSV to stdout |
| Every table as CSV |
| The db schema |
MCP server
uv run vitals-mcp starts a stdio MCP server with these tools: get_daily, get_workouts,
get_sleep, get_heart_rate, run_sql (read-only), get_schema, get_report, sync.
Register it in Claude Code:
claude mcp add --scope user vitals -- uv run --project /absolute/path/to/wannabe-fit vitals-mcpThen ask things like "compare my sleep on training days and rest days this month" or "show my heart rate during yesterday's run in 1-minute buckets". Any other MCP client takes the same command.
Files
data/vitals.db is plain SQLite. data/reports/latest.md is always the current report. Both are
gitignored, as is config.yaml, so a fork of this repo never carries personal data.
Tables
Raw tables keep epoch milliseconds in UTC. Zone offsets from Health Connect sit next to sessions.
daily uses the timezone from config.yaml. Sleep belongs to the day you woke up.
Table | Content |
| Every heart-rate sample: |
| Instant samples from the matching Health Connect tables |
| Interval totals, per source |
| Sessions and stage segments (1 awake, 4 light, 5 deep, 6 REM, 7 awake in bed) |
| Exercise sessions with the Health Connect type code, name, and source |
| Computed, rebuilt on every |
Several apps often write the same thing. Steps, calories, and distance take the largest single-source total per day rather than the sum. Auto-detected sessions from Google Fit are kept unless they overlap a session another app tracked for the same window. Both rules are configurable.
Formulas
The constants live in vitals/metrics.py.
HR max:
hr_maxin config, else Tanaka208 - 0.7 * age. Replace it with a measured peak when you have one. Every zone and strain number depends on it.Resting HR (
rhr,rhr_method): lowest 15-minute mean of HR inside the main sleep session. Fallbacks in order: lowest 15-minute mean of the whole day, the device's resting HR record, the 5th percentile of the day.Baselines:
rhr_base30andrhr_sd30are the trailing 30-day median and standard deviation, excluding today. Zones and TRIMP userhr_base30, so one bad night does not move every zone boundary.Zones: Karvonen heart-rate-reserve fractions from
config.yaml. z1 50-60%, z2 60-70%, z3 70-80%, z4 80-90%, z5 90-100%. Minutes come from each sample's gap to the next, capped athr_sample_cap_s(default 10 min) in the background and at 60 s inside workouts.TRIMP (Banister 1991):
sum(minutes * f * 0.64 * e^(1.92 f))for men,0.86 * e^(1.67 f)for women, wherefis the heart-rate-reserve fraction. Only samples withf >= 0.30count; sedentary time carries no load.trimp_edwardsissum(zone minutes * zone number).Strain (0-21):
21 * (1 - e^(-L / 155))withL = trimp_edwards. Logarithmic like Whoop, so the last points are the hardest to earn. About 60 minutes in z5 gives 18. This is not Whoop's private formula.HR calories (
kcal_hr, Keytel 2005): per minute, men(-55.0969 + 0.6309 HR + 0.1988 kg + 0.2017 age) / 4.184, only whilef >= 0.30. Validated for steady aerobic work; it overstates strength sessions. The device number sits next to it.Fitness, fatigue, form (
ctl,atl,tsb; Banister impulse-response): CTL is a 42-day exponential average of TRIMP, ATL a 7-day one, TSB is yesterday's CTL minus ATL.acwris the 7-day mean over the 28-day mean; above 1.5 is the injury-risk zone in the literature.Sleep:
sleep_asleep_minis the sum of light, deep, REM, and generic sleeping stages; time in bed is the session length.sleep_need_min = base + 60 * (yesterday's strain / 21) + min(60, 0.5 * 7-day debt).sleep_performanceis asleep over need, capped at 100.sleep_consistencyloses 25 points for each hour of circular standard deviation in bed and wake times over 7 days (the clock wraps at midnight).Recovery (0-100):
0.50 * RHR component + 0.35 * sleep performance + 0.15 * SpO2 component. The RHR component is70 - 20 * z, where z is today's RHR against the 30-day baseline. The SpO2 component maps the overnight minimum from 88% (0) to 95% (100). If HRV is present the weights become HRV 40, RHR 25, sleep 25, SpO2 10. Bands: green 67+, yellow 34-66, red below 34.VO2max (
vo2max_uth, Uth 2004):15.3 * HRmax / RHR. A population estimate, for trend only.Workout HR recovery (
hrr_1min): mean HR over the last 15 s of the session minus mean HR 50-75 s after it. Needs the watch to keep sampling after the workout ends. Above 20 bpm is good, below 12 poor.
Adapting it
Another watch: nothing to change if the app writes to Health Connect. Run
vitals doctoron the export to see which tables are filled. If the watch provides HRV, recovery picks it up on its own.Older history:
vitals ingestalso reads a Google Takeout export of Google Fit (the zip or theFit/All Datafolder). Fit auto-detected walks arrive as workouts with sourceFit.Different formulas: every constant is in
vitals/metrics.py, andcomputerebuilds the derived tables from raw samples, so changes apply to all history at once.Testing without a phone:
uv run python scripts/make_fixture.py /tmp/fixture.zipbuilds a synthetic 35-day export in the real Health Connect layout. Ingest it from/tmp, never fromdata/inbox, orsyncmixes synthetic rows into your real database.
Limits
Health Connect has no data type for vendor stress scores, so they do not export.
Background HR interval is whatever the watch app allows.
doctorprints the real median gap.Health Connect keeps 30 days of most data on the phone by default. Daily exports snapshot everything, so nothing is lost while the schedule runs.
The recovery score without HRV leans on resting HR, sleep, and SpO2. It is a trend instrument, not a diagnosis.
License
MIT.
Available Tools
8 toolsget_dailyB
Daily metrics (recovery, RHR, sleep, strain, TRIMP, CTL/ATL/TSB, SpO2, steps). ISO dates, newest first.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| date_to | No | ||
| date_from | No |
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 the full burden and only partially meets it. It usefully discloses the date format (ISO) and sort order (newest first), but says nothing about authorization, rate limits, how 'limit' interacts with the date range, or what an empty date_from/date_to means.
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 terse fragments, front-loaded with the returned metric set and followed by the format/ordering note. No filler, no restatement of the tool name.
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?
An output schema exists, so return values need not be explained. But with zero annotation coverage, zero schema description coverage, and no sibling differentiation, an agent still lacks enough to decide between this and the granular sleep/heart-rate tools or to reason about the default empty date bounds.
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 the description must compensate for all three parameters. It only clarifies date format and ordering, which partially covers date_from/date_to, and leaves 'limit' (default 30) and the empty-string date defaults entirely unexplained, including whether no limit is applied or capped.
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 names a specific resource (daily metrics) and enumerates the exact fields returned (recovery, RHR, sleep, strain, TRIMP, CTL/ATL/TSB, SpO2, steps), which is far more specific than the bare name. However, it does not clarify how this aggregated view relates to siblings like get_sleep or get_heart_rate, which appear to return overlapping data at finer granularity.
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 metric list implicitly signals the aggregated daily scope, which hints at when this is preferable to the granular get_sleep/get_heart_rate siblings. But there is no explicit when-to-use or when-not-to-use guidance, and nothing states that an empty date range is acceptable or what the defaults do.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_heart_rateB
Raw HR samples between two ISO timestamps (local tz), averaged into buckets. bucket_minutes=0 for raw.
| Name | Required | Description | Default |
|---|---|---|---|
| end_iso | Yes | ||
| start_iso | Yes | ||
| bucket_minutes | 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 does disclose genuinely useful behavior — local-timezone ISO timestamps, bucket averaging, and bucket_minutes=0 meaning raw — but says nothing about permissions, data volume, rate limits, or pagination.
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 telegraphic sentences with zero filler, front-loading the resource and its range scoping. Slightly under-specified rather than bloated.
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?
An output schema exists, so return values need not be described. However, for a 3-parameter tool with 0% schema coverage and no annotations, the description leaves meaningful gaps in parameter semantics and usage routing.
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 the description must compensate. It explains bucket_minutes (=0 for raw, with a default of 5 implied by the schema) and clarifies timezone handling for the timestamps, but leaves start_iso/end_iso inclusive/exclusive semantics and format details unstated.
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?
States a specific verb+resource: raw HR samples over a timestamp range, with bucketing behavior. It distinguishes itself from aggregation-oriented siblings like get_daily, though it never names them explicitly.
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?
No when-to-use guidance, no mention of prerequisites, and no routing to alternatives such as get_daily for aggregated views. The agent must infer that 'raw' implies low-level granularity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_reportC
Markdown report: today's card, daily table, workouts, 7-day means.
| 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?
With no annotations, the description carries the full behavioral burden, yet it discloses almost nothing: no mention of auth requirements, cost, whether it performs writes or triggers a sync, or how 'days' shapes the output. The only behavioral hint is the Markdown output format, and the output schema already covers the return shape.
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?
A single front-loaded line with no filler; every token names a section of the report. It is efficient, though so compressed that it reads as a fragment rather than a directive.
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?
An output schema exists, so return values need not be explained, but the description still omits the purpose of the days parameter and any routing guidance versus siblings. For a tool with one documented-only-in-schema input, that leaves meaningful gaps.
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% for the single 'days' parameter (default 14), and the description never explains it. Mentioning '7-day means' arguably hints at a fixed window inside the report, but the relationship to the days argument is left entirely unstated.
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 names the resource (a report) and enumerates its contents (today's card, daily table, workouts, 7-day means), which distinguishes it from siblings like get_daily and get_workouts that return single data streams. However it is a noun fragment with no explicit verb, so the 'what it does' is inferred rather than stated.
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?
No when-to-use guidance at all. It never says to prefer this over get_daily/get_workouts when a consolidated digest is wanted, nor does it name any alternative or exclusion. The agent must guess its place among eight siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_schemaA
CREATE TABLE statements for every table, plus the column list of daily and workout_metrics.
| 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?
No annotations exist, so the description carries the burden. It does disclose the shape of the returned content, including the useful nuance that two specific tables get column lists, but it says nothing about safety, permissions, or side effects (a reader must assume it is a harmless read). Adequate but incomplete for a zero-annotation tool.
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?
One sentence, front-loaded with the primary output (DDL statements) and appended with the secondary detail (column lists). Every clause 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?
An output schema exists, so return-value description is not strictly required, and the description already covers it. What remains missing is any link to the workflow (call this before run_sql), which would make it complete for a schema-introspection 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 tool takes no parameters, so there is no parameter meaning to convey and the baseline of 4 applies.
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 concretely states what the tool returns: CREATE TABLE statements for every table plus column lists for daily and workout_metrics. That is more specific than a generic 'get schema', but it never distinguishes itself from schema-related siblings like run_sql or get_report, which is the only thing keeping it from a 5.
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?
There is no when-to-use guidance. An agent reading only this description is not told that this is the tool to call before run_sql, nor when the column lists are preferable to the full DDL. Usage is left entirely to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_sleepC
Sleep sessions with stage minutes, efficiency, need, performance, debt, consistency.
| 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 behavioral burden, and it discloses almost nothing: no statement that this is a read-only query, no windowing/pagination behavior, no note that 'days' defaults to 14. Without annotations, the near-total absence of behavioral context is a real gap.
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?
It is a single short sentence with no filler, so it is efficient, but it is under-specified rather than genuinely concise. There is no front-loaded statement of purpose for the field list to elaborate on.
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?
An output schema already exists, so enumerating returned metrics is largely redundant, while the actually useful information ('days' semantics, default window, read-only nature) is missing. For a tool with a parameter and no annotations, the description is too thin to call it complete.
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 single parameter 'days' has 0% schema description coverage and a default of 14 that is never surfaced in the description. The description does not mention the parameter at all, so an agent gets no meaning for the time-range control beyond the bare name.
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 is a noun fragment listing returned fields ('Sleep sessions with stage minutes, efficiency, need, performance, debt, consistency') rather than a verb+resource statement. An agent can infer retrieval of sleep data, but the fragment never says what the tool does or over what scope. It also gives no differentiation from siblings like get_daily or get_report.
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?
There is no when-to-use guidance, no prerequisites, and no mention of alternative tools. Siblings such as get_daily, get_heart_rate, and get_report plausibly overlap with sleep data, yet the description never routes the agent between them.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_workoutsC
Per-workout metrics: HR avg/max, zone minutes, TRIMP, strain, HR-derived kcal, 1-min HR recovery.
| 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?
With no annotations provided, the description carries the full behavioral burden, yet it only lists output field names. It says nothing about read-only nature, permissions, rate limits, aggregation behavior, or whether workouts must be synced first (a sync sibling exists).
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?
It is a single terse fragment with no wasted words, so length is fine. However, it is under-specified rather than concise, and the informational content is not front-loaded around the tool's purpose.
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?
An output schema exists, so return values need not be re-explained, but the description omits the temporal scope that the 'days' parameter implies, making the tool's operating context incomplete. For a parameterized retrieval tool with zero annotation coverage, key context is missing.
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% and the description never mentions the sole 'days' parameter, its default of 30, or how the lookback window affects results. The field list adds no meaning about the input, leaving the parameter entirely undocumented.
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 names the resource (workout metrics) and enumerates the returned fields, but contains no verb and reads as a fragment rather than stating what the tool does. It does not distinguish itself from siblings like get_daily or get_heart_rate, which also return health metrics.
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?
There is no when-to-use guidance, no mention of the default 30-day window, and no routing vs alternatives such as get_daily or get_heart_rate. The agent gets no signal about the conditions under which this tool is the right choice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_sqlA
Read-only SQL against the vitals db. Use get_schema first.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
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 the full behavioral burden. It does disclose the most important trait — read-only execution — but says nothing about result-size limits, timeouts, whether writes/DDL are rejected, or what happens on error.
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 short sentences, zero filler, with the read-only constraint and the prerequisite front-loaded before any detail.
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?
An output schema exists, so return values needn't be described, and the read-only contract plus the get_schema prerequisite cover the essentials for a one-parameter tool. Only query-dialect and execution-limit context is missing.
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% and the single 'query' parameter has no description, so the schema adds nothing. The description partially compensates by implying the target database and that only read-oriented SQL is valid, but gives no syntax, dialect, or dialect-specific constraints.
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?
States a specific verb+resource ('SQL against the vitals db') and constrains the operation to read-only, which cleanly separates it from the predefined getters like get_daily and get_heart_rate. It doesn't explicitly name those siblings as alternatives, but the purpose is unambiguous.
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?
'Use get_schema first' gives a concrete prerequisite and names a sibling tool by name, which is real routing guidance. It stops short of saying when to prefer run_sql over the fixed query tools (get_daily, get_workouts), so no explicit when/when-not distinction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
syncB
Ingest the newest export in the inbox, recompute metrics, and rewrite reports/latest.md.
| 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 the full disclosure burden. It usefully reveals that reports/latest.md is overwritten and that data is read from an inbox export, which hints at destructive/state-changing behavior, but it omits permissions, idempotency, and what happens on failure or when the inbox is empty.
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?
One sentence, fully front-loaded with the action sequence and end target. Every clause earns its place with no filler.
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?
An output schema exists, so return values need not be explained, and a 0-param tool has no argument gaps. However, as a mutation with no annotations, the description should say more about the write target's lifecycle, error conditions, or required access, leaving a meaningful gap.
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 tool takes zero parameters, so per the baseline there is nothing for the description to document. The schema already states this fully, and the description correctly focuses on behavior rather than arguments.
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 uses three precise verbs (ingest, recompute, rewrite) against concrete artifacts (inbox export, metrics, reports/latest.md), so an agent immediately knows this is the pipeline that turns the newest export into refreshed reports. It does not name or contrast itself with any sibling, but the write-oriented pipeline is unmistakably distinct from the read-only get_* siblings.
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?
There is no stated condition for when to call this versus the read tools, and no prerequisites, ordering, or exclusions are given. The only implicit signal is that it is the sole mutating tool among the siblings, which the agent must infer.
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.
8 tool updates
v0.1.0- First observed
get_daily - First observed
get_heart_rate - First observed
get_report - First observed
get_schema - First observed
get_sleep - First observed
get_workouts - First observed
run_sql - First observed
sync
TDQS
Scored across 8 tools
Tools target distinct resources and actions: daily aggregates, workouts, sleep sessions, raw heart rate, schema, SQL, reports, and sync. Minor overlap exists because get_daily summarizes sleep and get_report aggregates daily/workout data, but descriptions make boundaries clear.
Six read tools follow get_<noun> (get_daily, get_workouts, get_sleep, get_heart_rate, get_schema, get_report), while run_sql and sync are action-oriented exceptions. Mostly consistent, though get_daily is slightly less noun-specific than the others.
Eight tools are well-scoped for a personal vitals and analytics MCP. The set covers core queries, schema inspection, reporting, and ingestion without bloat or obvious redundancy.
The surface covers daily metrics, workouts, sleep, raw HR, arbitrary SQL, schema inspection, reporting, and sync/ingest, leaving no obvious dead ends for read and analytics workflows. Manual CRUD is absent, but the server is designed around exports and recomputation.
Maintenance
Related MCP Connectors
- SomviaOAuthapp.somvia
Private Apple Health metrics and workout detail for ChatGPT, Claude, and any MCP client.
Multi-tenant hosted MCP server for Oura Ring — 21 read-only tools, OAuth per user.
- JotiOAuthcom.kompetic
Read your workouts, history, and stats; create and schedule new workouts. Writes are additive only.
MCP server for Withings health data — sleep, activity, heart, and body metrics.
Related MCP Servers
- AlicenseBqualityAmaintenanceDownloads all your Garmin health and fitness data into a local SQLite database and exposes 45 MCP tools for AI analysis, enabling assistants to query sleep, training load, HRV, and more.48149AGPL 3.0
- AlicenseBqualityAmaintenanceMCP server for local fitness-data extraction and analysis from Garmin Connect, Intervals.icu, and Strava. Provides read-only analytical tools over DuckDB and targeted Strava enrichment.35425 npmAGPL 3.0
- AlicenseCqualityAmaintenanceProvides local MCP tools to query your own Xiaomi Mi Fitness health data (sleep, workouts, heart rate, etc.) stored in a local SQLite database, enabling AI assistants to access personal health records without exposing credentials.1519AGPL 3.0
- AlicenseNot gradedqualityAmaintenanceMCP server that mirrors your Garmin data into a personal database and exposes tools for health summaries, training load, muscle readiness, and race analysis, with optional chat-driven insights via stdio or HTTP.1MIT