Skip to main content
Glama

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

recovery (0-100), recovery_band, rhr, rhr_method, rhr_base30, rhr_sd30, hrv_night_ms when the watch provides it

Sleep

sleep_start, sleep_end, sleep_tib_min, sleep_asleep_min, sleep_efficiency, light/deep/REM/awake minutes, sleep_need_min, sleep_performance, sleep_debt7_h, sleep_consistency

Load

strain (0-21), trimp, trimp_edwards, zone minutes z0..z5, ctl, atl, tsb, acwr

Body

spo2_night_mean, spo2_night_min, spo2_day_mean, weight_kg, vo2max_uth

Activity

steps, distance_km, kcal_hr, kcal_active_device, kcal_total_device

Quality

hr_samples, hr_minutes, hr_coverage_pct, hr_max_used

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, timezone

On the phone:

  1. 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.

  2. 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 set rclone.remote_path: "gdrive:<your folder>" in config.yaml. vitals sync then 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 card

If 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 hour

This 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 dev

Interfaces

Command line

Command

What it does

uv run vitals doctor [path]

Inventory of an export and of the local db, sample gaps, last sync

uv run vitals ingest [path]

Load a Health Connect zip or db, or a Takeout Fit zip or folder. Default: newest file in data/inbox

uv run vitals compute

Recompute daily and workout_metrics

uv run vitals sync

rclone pull (if configured) + ingest + compute + report

uv run vitals today

The latest day card

uv run vitals report --days 30

Markdown report with daily and workout tables

uv run vitals sql "select ..."

Read-only SQL, CSV to stdout

uv run vitals export DIR

Every table as CSV

uv run vitals schema

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-mcp

Then 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

hr

Every heart-rate sample: ts, bpm, source

spo2, hrv, resp, rhr_device, weight, vo2_device

Instant samples from the matching Health Connect tables

steps, calories, distance

Interval totals, per source

sleep_sessions, sleep_stages

Sessions and stage segments (1 awake, 4 light, 5 deep, 6 REM, 7 awake in bed)

workouts

Exercise sessions with the Health Connect type code, name, and source

daily, workout_metrics

Computed, rebuilt on every compute

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_max in config, else Tanaka 208 - 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_base30 and rhr_sd30 are the trailing 30-day median and standard deviation, excluding today. Zones and TRIMP use rhr_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 at hr_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, where f is the heart-rate-reserve fraction. Only samples with f >= 0.30 count; sedentary time carries no load. trimp_edwards is sum(zone minutes * zone number).

  • Strain (0-21): 21 * (1 - e^(-L / 155)) with L = 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 while f >= 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. acwr is the 7-day mean over the 28-day mean; above 1.5 is the injury-risk zone in the literature.

  • Sleep: sleep_asleep_min is 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_performance is asleep over need, capped at 100. sleep_consistency loses 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 is 70 - 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 doctor on the export to see which tables are filled. If the watch provides HRV, recovery picks it up on its own.

  • Older history: vitals ingest also reads a Google Takeout export of Google Fit (the zip or the Fit/All Data folder). Fit auto-detected walks arrive as workouts with source Fit.

  • Different formulas: every constant is in vitals/metrics.py, and compute rebuilds 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.zip builds a synthetic 35-day export in the real Health Connect layout. Ingest it from /tmp, never from data/inbox, or sync mixes 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. doctor prints 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 tools
get_dailyB

Daily metrics (recovery, RHR, sleep, strain, TRIMP, CTL/ATL/TSB, SpO2, steps). ISO dates, newest first.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
date_toNo
date_fromNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
end_isoYes
start_isoYes
bucket_minutesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.2/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.7/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.4/5.0
Behavior2/5

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.

Conciseness3/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.4/5.0
Behavior2/5

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.

Conciseness3/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

  1. 8 tool updatesv0.1.0
    • First observedget_daily
    • First observedget_heart_rate
    • First observedget_report
    • First observedget_schema
    • First observedget_sleep
    • First observedget_workouts
    • First observedrun_sql
    • First observedsync

TDQS

B3.3/5.0

Scored across 8 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness5/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    A
    maintenance
    Downloads 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.
    48
    149
    AGPL 3.0
  • A
    license
    B
    quality
    A
    maintenance
    MCP 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.
    35
    425 npm
    AGPL 3.0
  • A
    license
    C
    quality
    A
    maintenance
    Provides 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.
    15
    19
    AGPL 3.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP 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.
    1
    MIT