Quantified Self MCP Server
This server lets AI agents read, log, update, and delete personal daily health metrics (steps, sleep, resting heart rate), raw body measurements (e.g., weight), and workout sessions from a local SQLite database.
Read health data: Retrieve daily metrics (steps, sleep, resting heart rate) for a date range.
Read finance data: Retrieve categorized expenses from a local finance ledger (note: not mentioned in README, but present in schema).
Log daily metrics: Create or update a day's steps, sleep hours, or resting heart rate.
Update daily metrics: Modify existing daily metric entries, including clearing individual fields.
Clear daily metrics: Delete a single day's metrics or bulk-delete a date range.
Log measurements: Record timestamped body measurements like weight or body fat percentage.
Update/delete measurements: Edit or remove individual measurement rows by ID.
Log workouts: Record workout sessions with type, duration, calories, and notes.
Update/delete workouts: Edit or remove workout sessions by ID.
No analytics tools: Unlike the README's described tools (e.g.,
get_baseline,detect_metric_anomalies), the server schema only exposes data entry/retrieval/update/delete tools. Complex analytics are not available in this schema.
Allows importing health data from Apple Health export files (XML) into the local database, as well as reading and logging daily health metrics.
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., "@Quantified Self MCP ServerWhat was my average sleep and steps last week?"
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.
Quantified Self MCP
Your health data. Your AI. Your machine.
Quantified Self MCP is a privacy-first Model Context Protocol (MCP) server that gives AI agents controlled access to your personal health data stored locally.
Built with Python, FastMCP, and SQLite, it works with both local LLMs and cloud-based LLMs. You choose where your AI runs.
What Is It?
Quantified Self MCP connects an AI agent to your personal health data through the Model Context Protocol (MCP).
The MCP server does not require a specific AI provider.
You can run the entire AI stack locally, or connect the server to an online model when you prefer.
Related MCP server: apple-health-mcp
๐ Local AI or โ๏ธ Cloud AI
The important distinction is between the MCP server and the AI model.
Fully Local
With a local MCP-compatible agent and local LLM, your health data and AI inference can remain on your machine.
Cloud LLM
You can also connect the same MCP server to a hosted model.
In that setup, your database and MCP server remain local, while data returned by MCP tools may be sent to the cloud model provider.
The choice is yours.
Quantified Self MCP does not lock you into Claude, OpenAI, or any other model provider.
๐ Privacy First
Your health data is stored locally in SQLite, and the MCP server runs on your machine.
The server itself does not require a cloud database, account, or hosted data store.
For maximum privacy, use a local LLM so the entire pipeline can remain on your machine.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ YOUR MACHINE โ
โ โ
โ Health Data โ
โ โ โ
โ Local SQLite โ
โ โ โ
โ Quantified Self MCP โ
โ โ โ
โ Local AI Agent โ
โ โ โ
โ Local LLM โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโOptional Private Fields
If specific metrics should never be returned to the model, configure:
HEALTH_PRIVATE_FIELDS=weight_kg,moodPrivate fields can still be stored and logged, but MCP read operations return them as null.
This gives you another layer of control over which health metrics an AI agent can access.
โค๏ธ What Can It Track?
Quantified Self MCP currently supports:
๐ Daily steps
๐ด Sleep duration
โค๏ธ Resting heart rate
โค๏ธ Heart rate
๐ Heart-rate variability (HRV)
โ๏ธ Weight
๐๏ธ Workout minutes
๐ Mood
๐ง Water intake
Every metric is optional, so you can track only the measurements you actually use.
๐ฌ What Can You Ask?
Once connected to an MCP-compatible AI agent, you can ask questions naturally.
For example:
How has my sleep changed over the last 30 days?What was my average step count this week?Show me my resting heart rate trend.How much water did I drink on average this month?What patterns do you see in my recent health data?You can also log information through the AI agent:
Log 7.5 hours of sleep for today.Or correct a mistake:
Clear today's mood entry.๐ง MCP Tools
The server exposes eighteen MCP tools, organized in three layers:
Layer 1 โ Data
Tool | Purpose |
| Read all health metrics for a selected date range |
| Read a single metric's day-by-day values for a date range |
| Record one or more health metrics for a specific day |
| Clear a single metric without affecting other data |
| Write a date range of metrics to a local CSV file |
| Retrieve provenance information for a health metric and its source data |
export_health_data_csv writes straight to disk next to the database and returns only the file's path and a row count โ not the row values themselves โ so exporting a long history doesn't have to pass through a cloud LLM's context just to get a file you can open elsewhere.
Layer 1b โ Raw measurements & workout sessions
Tool | Purpose |
| Record one raw observation (metric, value, timestamp, source) instead of a day total |
| Read individual measurement rows, most recent first, filterable by metric/source |
| Record one workout as a structured event (activity, duration, intensity, heart rate) |
| Read individual workout sessions, most recent day first |
| Roll up a day's raw measurements into that day's |
Use log_measurement/log_workout_session when the source, exact time, or multiple same-day readings matter (e.g. two wearables both logging heart rate); aggregate_measurements then folds those into daily_metrics so every Layer 2/3 tool below can use them.
Tool routing
Which tool to call for a given request:
Record data
User intent | Tool |
Simple daily metric (steps, weight, mood, ...) |
|
Individual timestamped/sourced measurement |
|
Workout / exercise session |
|
Read data
User intent | Tool |
Broad health data across metrics |
|
Raw individual measurement rows |
|
Workout / exercise sessions |
|
One metric's history/trend over time |
|
Each of these tools' own MCP description also states this explicitly ("Use this tool when" / "Do not use this tool when", with the alternative named), and the server's top-level MCP instructions repeat the same routing model โ so the boundary is visible whether an agent reads one tool's schema or the whole tool list.
Layer 2 โ Analytics (statistics computed over one or two metrics; see analytics.py)
Tool | Purpose |
| Mean/median/stdev for a metric over a window โ "what's normal" |
| Flag days that deviate sharply from a metric's own baseline |
| Fit a straight-line trend (direction, slope, rยฒ) over a window |
| Compare a metric's average between two date ranges |
| Pearson correlation between two metrics, with optional lag |
Layer 3 โ Personal intelligence (composes Layer 2, returns facts rather than prose โ the calling model still does the narration)
Tool | Purpose |
| Scan every metric for notable shifts, anomalies, or trends recently |
| Build an evidence bundle for "why did X look like that on this day?" |
The server also exposes read-only MCP resources for health metric schemas and individual days.
All data operations are scoped to the supported health metrics. The server does not expose arbitrary SQL execution to the model. Any metric listed in HEALTH_PRIVATE_FIELDS is refused by every Layer 2/3 tool outright (not just redacted afterward), since a baseline or anomaly computed from a private metric would leak its shape even without ever printing a raw value.
๐ Documentation Source of Truth
The MCP server implementation is the authoritative source for its available tools and schemas.
Because MCP clients and directories such as Glama inspect the running server directly, manually maintained tool lists can become outdated as new tools and metrics are added.
The project therefore treats the registered MCP tools and their schemas as the source of truth for tool documentation.
Tool documentation should be generated from the server's registered tools rather than maintained independently wherever practical.
A documentation check should ensure that:
MCP Server
โ
Registered Tools
โ
Generated Documentation
โ
README / TOOLS.mdremain synchronized.
This prevents discrepancies between:
Actual implementation
โ
GitHub documentation
โ
MCP directory inspectionand makes the available MCP interface easier for users, contributors, AI agents, and MCP directories to understand.
๐ฅ Import Your Health Data
You can initialize the local database from CSV data.
quantified-self-init-db sample_data/health_sample.csvThe supported health fields include:
date
steps
sleep_hours
resting_heart_rate
heart_rate
hrv_ms
weight_kg
workout_minutes
mood
water_mlA handful of common alternate header spellings are also recognized
automatically, so you don't need to rename columns or know this
project's exact names first โ e.g. step_count, hr, bpm, weight,
sleep, hrv. Anything else can still be mapped with --map COLUMN=HEADER (see init_db.py's COLUMN_ALIASES for the full alias
list, and its module docstring for --map).
You can also import an Apple Health export:
quantified-self-init-db export.xmlThe importer maps supported Apple Health records into the local database.
Android users: Health Connect doesn't have a built-in export button like Apple Health, so it needs one extra step โ see docs/clients/android-health-connect.md.
See exactly what an import found, imported, skipped, and ignored
Add --report to any import to print a full breakdown instead of just a
one-line summary:
quantified-self-init-db export.xml --reportIMPORT COMPLETE
Source: apple-health (export.xml)
Date range: 2025-03-01 -> 2026-09-13
Imported:
Steps 420 day(s)
Sleep 398 day(s)
Heart Rate 410 day(s)
HRV 180 day(s)
Weight 30 day(s)
Skipped: 42 record(s)
Unsupported: 18 record type(s), 6,204 record(s) total
HKQuantityTypeIdentifierBloodPressureSystolic: 3,102
HKCategoryTypeIdentifierMindfulSession: 890
..."Skipped" is records this import tried and failed to parse (bad date/value โ see the warnings printed alongside). "Unsupported" is record types the importer doesn't map to any column at all โ nothing here is silently lost; it's counted and named so you know what a fuller importer would need to add.
โก Installation
PyPI
pip install quantified-self-mcpThis installs:
quantified-self-mcp
quantified-self-init-dbFrom Source
git clone https://github.com/Thecimal/quantified-self-mcp.git
cd quantified-self-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtDocker
docker build -t quantified-self-mcp .The included Docker configuration can be used for containerized MCP deployments, including Glama.
๐ Quick Start
1. Install
pip install quantified-self-mcp2. Load your health data
quantified-self-init-db your-health-data.csv3. Connect the MCP server
Pick your client and follow the tested, step-by-step guide โ each one takes you from a fresh clone to an answered question in about 5 minutes:
Client | Guide |
Claude Desktop | |
LM Studio (local models) | |
Open WebUI |
Any other MCP-compatible client works too โ point it at server.py the same way, using the .venv Python interpreter.
4. Ask your health data questions
How has my sleep changed over the last 30 days?The AI agent retrieves the relevant health data through MCP and analyzes it.
๐ MCP Client Compatibility
Quantified Self MCP uses the standard Model Context Protocol, so the server is designed to work with MCP-compatible clients and models rather than being tied to a single AI application.
The project includes configuration for clients supported by FastMCP, and standard MCP configuration can be generated for other compatible clients.
For local AI setups, pair the server with an MCP-compatible client and a local LLM runtime.
For example:
Local LLM
+
MCP-compatible Agent
+
Quantified Self MCPThis allows the complete AI workflow to remain local.
๐๏ธ Architecture
โโโโโโโโโโโโโโโโโโโโโโ
โ AI Agent โ
โโโโโโโโโโโฌโโโโโโโโโโโ
โ
MCP Protocol
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโ
โ Quantified Self โ
โ MCP โ
โ โ
โ FastMCP โ
โโโโโโโโโโโฌโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโ
โ Local SQLite โ
โ โ
โ Health Data โ
โโโโโโโโโโโโโโโโโโโโโโThe AI model and the MCP server are separate components.
This means you can change the AI model without changing how your health data is stored or exposed.
๐ ๏ธ Technology
Component | Technology |
Language | Python |
Protocol | Model Context Protocol |
MCP Framework | FastMCP |
Database | SQLite |
Containerization | Docker |
CI | GitHub Actions |
Package | PyPI |
๐งช Development
Clone the repository:
git clone https://github.com/Thecimal/quantified-self-mcp.git
cd quantified-self-mcpCreate a virtual environment:
python3 -m venv .venv
source .venv/bin/activateInstall dependencies:
pip install -r requirements-dev.txtRun tests:
pytestBuild the package:
python -m buildGitHub Actions validates the project in a clean environment.
๐ Project Structure
quantified-self-mcp/
โโโ .github/
โ โโโ workflows/
โโโ sample_data/
โโโ tests/
โโโ Dockerfile
โโโ fastmcp.json
โโโ glama.json
โโโ init_db.py
โโโ logic.py
โโโ import_adapters.py
โโโ server.py
โโโ pyproject.toml
โโโ requirements.txt
โโโ requirements-dev.txt
โโโ SECURITY.md
โโโ CONTRIBUTING.md
โโโ CODE_OF_CONDUCT.md
โโโ CHANGELOG.md
โโโ llms.txt
โโโ LICENSE
โโโ README.md๐ก๏ธ Security
Health information is sensitive personal data.
Never commit:
Personal health records
Private SQLite databases
API keys
Passwords
Authentication tokens
Other sensitive personal information
For security vulnerabilities, please follow the instructions in SECURITY.md.
โญ Glama
Quantified Self MCP is available through the Glama MCP directory.
Glama Score
A / A / A
Category | Score |
License | A |
Quality | A |
Maintenance | A |
The project is listed as a Python / Local MCP server on Glama. Glama performs its own inspection of the MCP server and may expose the current registered tools and schemas directly.
Because the server implementation is the source of truth, the Glama inspection may reflect newly registered tools or metrics before corresponding manually written documentation has been updated.
View Quantified Self MCP on Glama โ
๐ค Contributing
Contributions, bug reports, documentation improvements, and ideas are welcome.
Before contributing, please read:
If you find a bug, please open an issue with enough information to reproduce it.
๐ License
MIT License.
Links
Quantified Self MCP
Your health data. Your AI. Your machine.
Available Tools
18 toolsaggregate_measurementsAggregate measurements into a dayAIdempotent
Roll up one day's raw measurements into that day's daily_metrics row, so existing analytics tools (which all read daily_metrics) benefit from data logged via log_measurement. Steps/water/workout_minutes sum across the day, resting_heart_rate/mood average, weight_kg takes the latest reading โ see logic.MEASUREMENT_AGGREGATION.
If a metric has measurements from more than one source that day (e.g. an Apple Watch and a Garmin both logging resting_heart_rate), use get_metric_provenance first to see whether they actually disagree, then pass source_priority to pick a winner rather than blending two devices' readings into one meaningless average.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | The day to aggregate, formatted YYYY-MM-DD. | |
| source_priority | No | Ordered list of source names, e.g. ["Apple Watch", "Garmin"]. For any metric with more than one source that day, the first name in this list that's actually present wins and the other source's readings for that metric are dropped from the aggregate. Omit to fall back to whichever source was imported most recently. |
Output Schema
| Name | Required | Description |
|---|---|---|
| row | Yes | |
| date | Yes | |
| aggregated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint=false, idempotentHint=true), it discloses the per-metric aggregation semantics (sums, averages, latest reading), the fallback to most-recently-imported source when source_priority is omitted, and a privacy caveat about data reaching a cloud model. That is rich behavioral context an agent cannot get from structured fields.
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?
Front-loaded with the core action and aggregation rules, then the multi-source caveat, then the privacy note. Every section is useful but the privacy paragraph is lengthy relative to the calling decision, making it slightly heavier than needed.
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, and the description covers aggregation logic, the alternative tool, parameter semantics, and the privacy implication. Nothing needed to invoke it correctly 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 100%, so baseline is 3, but the description adds genuine meaning: it explains why source_priority exists ('rather than blending two devices' readings into one meaningless average') and the resolution order, which the schema states tersely as first-present-wins.
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 and resource ('Roll up one day's raw measurements into that day's daily_metrics row') and explicitly frames the downstream beneficiary (analytics tools reading daily_metrics). It also distinguishes itself from log_measurement, which is named as the source of the data being aggregated.
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?
Gives clear context: run it to make log_measurement data visible to analytics, and route to get_metric_provenance first when multiple sources disagree, then use source_priority to pick a winner. No explicit 'when not to use' but the multi-source branch is a genuine alternative-selection rule.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calculate_metric_trendCalculate metric trendARead-onlyIdempotent
Fit a simple straight-line trend to one metric over a window and report its direction, slope (change per day), and r_squared (how well a straight line actually fits โ low r_squared means "noisy," not "flat").
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes | One of steps, sleep_hours, resting_heart_rate, weight_kg, workout_minutes, mood, water_ml, heart_rate, hrv_ms. | |
| end_date | No | Last day to include, formatted YYYY-MM-DD. Defaults to today. | |
| start_date | No | First day to include, formatted YYYY-MM-DD. Defaults to 30 days before end_date. |
Output Schema
| Name | Required | Description |
|---|---|---|
| range | Yes | |
| trend | Yes | |
| metric | Yes | |
| evidence | Yes | Coverage/quality of the data a single-metric analytical result is based on. See evidence.build_evidence for how each field is computed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and idempotentHint, covering the safety profile. The description adds meaningful context beyond annotations by explaining how to interpret r_squared ('noisy' vs 'flat') and disclosing a privacy consideration about data being sent to a cloud model.
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 core functionality is front-loaded in one concise sentence, followed by a clearly separated and relevant privacy note. Every sentence earns its place with no wasted words.
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?
For a read-only, idempotent tool with an output schema covering return values, the description is nearly complete. It explains output interpretation and privacy implications. The only minor gap is the lack of explicit guidance on when to choose it over sibling analysis tools.
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 100%, so the schema already documents all three parameters. The description does not add extra parameter-level details beyond what the schema provides, so a baseline score of 3 is appropriate.
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 and resource: 'Fit a simple straight-line trend to one metric over a window' and lists the exact outputs (direction, slope, r_squared). This clearly distinguishes it from sibling tools like detect_metric_anomalies or compare_metric_periods.
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 its usage context: when you want a simple trend line for one metric over a window. It does not explicitly mention alternatives or exclusions, but the purpose is clear enough that an agent can decide when to choose it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
clear_metricClear a single metricADestructiveIdempotent
Blank out (set to null) a single metric for a single day, without touching that day's other metrics. The counterpart to log_daily_metric for undoing a bad value โ e.g. a mood logged for the wrong day, or a weight entered with the wrong units.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | The day to clear a field for, formatted YYYY-MM-DD. | |
| field | Yes | Which metric to blank out. One of: steps, sleep_hours, resting_heart_rate, weight_kg, workout_minutes, mood, water_ml, heart_rate, hrv_ms. |
Output Schema
| Name | Required | Description |
|---|---|---|
| row | No | |
| note | No | |
| cleared | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is covered. The description adds real value beyond that: it specifies the mutation is field-scoped ('without touching that day's other metrics') and discloses a privacy trait (returned data flows into the model conversation, with cloud-vs-local implications) that no structured field conveys.
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?
Front-loads the core action and scope in the first sentence, then a concrete example, then the privacy note. The privacy paragraph is longer than strictly needed for tool selection but is meaningful content rather than filler, so only minor trimming is warranted.
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, and annotations carry the safety profile. The description fills the remaining gaps โ scope of the mutation, the use case, and the privacy caveat โ so an agent has everything needed to call it 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 100%, and the schema already documents both parameters with the YYYY-MM-DD format and the full field list. The description adds no additional syntax or semantics beyond what the schema provides, so the baseline 3 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?
States a specific verb and effect ('Blank out (set to null) a single metric for a single day') plus an explicit scope constraint ('without touching that day's other metrics'). It also names the sibling it complements (log_daily_metric), so an agent can distinguish it from the other 15 tools immediately.
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?
Gives a clear trigger context ('undoing a bad value โ e.g. a mood logged for the wrong day, or a weight entered with the wrong units') and names the counterpart tool log_daily_metric. It stops short of stating when NOT to use it (e.g. correcting vs. clearing), so it is clear but not fully exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
compare_metric_periodsCompare two periodsBRead-onlyIdempotent
Compare one metric's average between two date ranges โ e.g. "this month vs. last month" or "since starting a new medication vs. before." The two ranges may be any length and need not be adjacent or equal in size; each is summarized with its own baseline first.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes | One of steps, sleep_hours, resting_heart_rate, weight_kg, workout_minutes, mood, water_ml, heart_rate, hrv_ms. | |
| period_a_end | Yes | ||
| period_b_end | Yes | ||
| period_a_start | Yes | ||
| period_b_start | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| delta | No | |
| metric | Yes | |
| period_a | Yes | |
| period_b | Yes | |
| pct_change | No | |
| period_a_stats | Yes | |
| period_b_stats | Yes | |
| period_a_evidence | Yes | Coverage/quality of the data a single-metric analytical result is based on. See evidence.build_evidence for how each field is computed. |
| period_b_evidence | Yes | Coverage/quality of the data a single-metric analytical result is based on. See evidence.build_evidence for how each field is computed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavioral context beyond the annotations: it states that returned data becomes part of the conversation sent to the model, warns about cloud privacy implications, and explains that each period is summarized with its own baseline. The readOnlyHint and idempotentHint annotations align with the description, and no contradiction is present. It does not fully detail output behavior, but the output schema covers that.
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 reasonably concise and front-loaded: the first sentence states the core purpose and examples, the second addresses period flexibility, and the privacy note is a separate relevant addition. No filler sentences are present, and every part contributes to correct usage. It could be slightly shorter, but the added context is worthwhile.
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 presence of an output schema reduces the need to describe return values, and the description covers the main purpose, period flexibility, and privacy implications. However, the description does not compensate for the poorly documented date parameters nor explicitly guide selection against sibling analytical tools. For a tool with five required parameters and a low schema coverage, this leaves some 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 only 20%, with four date-range parameters entirely undocumented. The description adds some meaning by explaining that these are date ranges and that they may have arbitrary lengths, but it fails to specify date formats, inclusivity of endpoints, or the semantics of the start/end relationship. This is a notable gap given the schema's low coverage.
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 a specific verb and resource: 'Compare one metric's average between two date ranges.' It also provides concrete examples that clarify the intended use. However, it does not explicitly differentiate itself from sibling tools like calculate_metric_trend or find_metric_correlation, relying on the phrase 'average between two date ranges' to imply the distinction.
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 gives illustrative use cases ('this month vs. last month') and clarifies that periods need not be adjacent or equal. But it does not explicitly state when to prefer this tool over a sibling, nor does it mention any alternatives or exclusions. Usage guidance is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
detect_metric_anomaliesDetect metric anomaliesARead-onlyIdempotent
Flag days where one metric deviated sharply from its own baseline over the window, using a median/MAD-based modified z-score rather than a mean/stdev z-score โ more robust for short, noisy personal-health series, where the mean/stdev version is easily dragged around by the very outliers it's supposed to catch.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes | One of steps, sleep_hours, resting_heart_rate, weight_kg, workout_minutes, mood, water_ml, heart_rate, hrv_ms. | |
| end_date | No | Last day to include, formatted YYYY-MM-DD. Defaults to today. | |
| threshold | No | Modified z-score cutoff. 3.5 (the default, Iglewicz & Hoaglin's standard value) flags only clear outliers; lower it (e.g. 2.5) to see more borderline days. | |
| start_date | No | First day to include, formatted YYYY-MM-DD. Defaults to 90 days before end_date. |
Output Schema
| Name | Required | Description |
|---|---|---|
| range | Yes | |
| metric | Yes | |
| evidence | Yes | Coverage/quality of the data a single-metric analytical result is based on. See evidence.build_evidence for how each field is computed. |
| anomalies | Yes | |
| threshold | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and idempotentHint=true, so the safety profile is covered. The description adds valuable behavioral context: the statistical method (median/MAD modified z-score), the robustness rationale, and a privacy note about data being sent to the model. The privacy note is especially useful because it warns the agent that returned data may leave the local server, which is a behavioral trait not captured by annotations.
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 well-structured: the core purpose is front-loaded in the first sentence, followed by the statistical rationale and a privacy note. The privacy note is somewhat long but earns its place because it discloses a significant behavioral trait. No wasted words, though the statistical explanation could be slightly tighter.
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 an output schema, so return values are already documented. The description covers the method, the rationale, and the privacy implication. It doesn't mention edge cases like what happens when there's insufficient data, but for a read-only anomaly detection tool with full schema coverage, this is a minor 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?
Schema description coverage is 100%, so the schema already documents all four parameters. The description adds context about the threshold (Iglewicz & Hoaglin's standard value) and the default window (90 days), but these are also partially in the schema. The description doesn't add much beyond what the schema provides, so a baseline 3 is appropriate.
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 states a specific verb ('Flag'), a specific resource ('days where one metric deviated sharply from its own baseline'), and a precise method (median/MAD-based modified z-score). It clearly distinguishes itself from sibling tools like get_baseline or calculate_metric_trend by focusing on anomaly detection rather than summary statistics or trend calculation.
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 explains why this tool exists (robust for short, noisy personal-health series) and why the mean/stdev version is inferior, giving an agent a clear sense of when to use it. It doesn't explicitly name sibling alternatives or state when not to use it, but the context is strong enough that an agent can infer it's for anomaly detection rather than general queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
explain_metric_changeExplain a metric changeARead-onlyIdempotent
Build an evidence bundle for "why did my look like that on ?": that day's value against a 90-day baseline, whether it qualifies as an anomaly, the trend leading into it, and any other metric that correlates with it strongly enough to be worth mentioning. Returns facts, not an explanation โ turning "sleep was 2.6 stdev below baseline and resting heart rate correlates at r=0.71" into an actual answer for the person is what the calling model should do with these facts, not something this tool guesses at itself.
Do not use this tool when:
scanning across many metrics for what changed lately, without a specific metric/date in mind -> use
get_recent_changesinstead.you only need one piece of this bundle (just the baseline, just the trend, just anomalies, or just a correlation) rather than the full why-explanation -> use
get_baseline,calculate_metric_trend,detect_metric_anomalies, orfind_metric_correlationdirectly instead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | The day to explain, formatted YYYY-MM-DD. | |
| metric | Yes | One of steps, sleep_hours, resting_heart_rate, weight_kg, workout_minutes, mood, water_ml, heart_rate, hrv_ms. |
Output Schema
| Name | Required | Description |
|---|---|---|
| date | Yes | |
| trend | Yes | |
| value | No | |
| metric | Yes | |
| baseline | Yes | |
| sessions | No | |
| is_anomaly | Yes | |
| baseline_range | Yes | |
| trend_evidence | Yes | Coverage/quality of the data a single-metric analytical result is based on. See evidence.build_evidence for how each field is computed. |
| narrative_facts | Yes | |
| modified_z_score | No | |
| baseline_evidence | Yes | Coverage/quality of the data a single-metric analytical result is based on. See evidence.build_evidence for how each field is computed. |
| correlated_metrics | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and openWorldHint=false, and the description does not contradict them. It adds valuable behavioral context: the tool intentionally returns facts and not explanations, delegates the interpretive step to the calling model, and includes a privacy note that returned data becomes part of the conversation sent to the configured model.
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 longer than average, but every sentence earns its place: the core purpose is front-loaded, the returns-facts-not-explanation distinction is critical, the usage exclusions are concrete, and the privacy note is operationally relevant. There is no filler or repetition of schema fields.
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 rich output schema, the two parameters with full schema coverage, and annotations that already convey safety/idempotency, the description covers all remaining context an agent needs: when to use the tool, what it will and won't do, what data feeds into it, and privacy implications. Nothing important 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 100%, so the baseline is 3, but the description adds semantic meaning beyond the schema: it clarifies that 'date' anchors the 90-day baseline comparison and 'metric' is the subject of anomaly, trend, and correlation analysis. The example ('sleep was 2.6 stdev below baseline...') further illustrates how the two parameters are interpreted.
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 verb and resource ('Build an evidence bundle' for a metric/date), then enumerates exactly what the bundle contains: value vs 90-day baseline, anomaly status, trend, and correlated metrics. It also distinguishes itself from sibling tools by stating it returns facts rather than a synthesized explanation, so an agent can tell it apart from get_recent_changes and the individual metric 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 gives explicit 'Do not use this tool when' conditions and names the correct alternatives for each case (get_recent_changes for scanning, and get_baseline/calculate_metric_trend/detect_metric_anomalies/find_metric_correlation for single-piece needs). This leaves no ambiguity about when to invoke 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.
export_health_data_csvExport health data to CSVARead-only
Write daily health metrics for a date range to a CSV file on disk, next to the database, instead of returning every row through this tool's own result.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
Unlike read_health_data, this is not capped at MAX_ROWS_RETURNED and the row values themselves are not included in this tool's response โ only the resulting file's path and a row count are. That means a long-range export doesn't have to pass through a cloud LLM's context just to produce a file you can open yourself (in a spreadsheet, a notebook, another tool, etc.). Any metric listed in HEALTH_PRIVATE_FIELDS is still written as an empty cell in the file, since those fields shouldn't leave the database at all, not just stay out of the model's context.
| Name | Required | Description | Default |
|---|---|---|---|
| end_date | No | Last day to include, formatted YYYY-MM-DD. Defaults to today. | |
| start_date | No | First day to include, formatted YYYY-MM-DD. Defaults to 30 days before end_date. Ranges over ~10 years are rejected. |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | |
| range | Yes | |
| rows_exported | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: it discloses that returned data enters the conversation/cloud-model context, that HEALTH_PRIVATE_FIELDS are written as empty cells, and that the response contains only a path and row count rather than row values. These are non-obvious behavioral traits an agent cannot infer from readOnlyHint/openWorldHint alone. Note the tension with readOnlyHint=true for a tool that writes a file to disk, but the write is to the filesystem, not the source data, and the description is transparent rather than misleading.
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?
Purpose is front-loaded in the first sentence, followed by rationale and privacy caveats. It runs long for a two-parameter export tool, and minor details like 'next to the database' add little, but nearly every sentence carries real information (privacy, private-field handling, response shape).
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?
Output schema exists, so return values needn't be explained, yet the description still clarifies the response shape (path + row count). Combined with the privacy note and private-field handling, an agent has everything needed to call it and interpret the result 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 100% (both date params documented with format and defaults), so the schema carries the parameter burden. The description only references 'a date range' generically and adds no format or constraint detail beyond the schema's own ~10-year rejection note. Baseline 3 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?
States a specific verb+resource+output: 'Write daily health metrics for a date range to a CSV file on disk... instead of returning every row through this tool's own result.' It directly contrasts with read_health_data, so an agent can tell it apart from the read tools without opening a schema.
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?
Names the sibling alternative (read_health_data) and the condition that differentiates it: not capped at MAX_ROWS_RETURNED, and a long-range export needn't pass through the LLM's context. It stops short of an explicit routing rule ('use this instead when...'), but the context is clear enough to choose correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_metric_correlationFind correlation between two metricsARead-onlyIdempotent
Compute the Pearson correlation between two metrics over the same window, joined by date. Correlation, not causation: a strong r just means the two moved together, not that one caused the other.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| end_date | No | Last day to include, formatted YYYY-MM-DD. Defaults to today. | |
| lag_days | No | Shift metric_b this many days later before joining โ 1 tests whether metric_a today predicts metric_b tomorrow (e.g. "does poor sleep tonight predict lower steps tomorrow?"). 0 (default) compares same-day values. | |
| metric_a | Yes | ||
| metric_b | Yes | ||
| start_date | No | First day to include, formatted YYYY-MM-DD. Defaults to 90 days before end_date. |
Output Schema
| Name | Required | Description |
|---|---|---|
| n | Yes | |
| r | No | |
| note | No | |
| lag_days | Yes | |
| metric_a | Yes | |
| metric_b | Yes | |
| evidence_a | No | |
| evidence_b | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint=false. The description adds useful behavioral context: the caveat that correlation does not imply causation, the fact that metrics are joined by date, and a privacy warning about data becoming part of the conversation sent to the model. These go beyond the annotations without contradicting them.
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 main purpose is front-loaded in the first sentence, followed by a brief interpretation caveat and a privacy note. Both additions are relevant and concise, though the privacy note is somewhat longer than typical tool descriptions.
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?
With an output schema present and annotations covering read-only/idempotent behavior, the description does not need to explain return formats. It covers interpretation, data handling, and the join behavior. It could be more complete by explicitly naming sibling alternatives, but for a unique correlation tool this is a minor 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 schema already documents start_date, end_date, and lag_days with formats, defaults, and a worked example. The description adds the general idea of joining two metrics by date, but does not add meaning to metric_a or metric_b beyond their self-evident roles. Schema coverage is 60%, and the missing parameter descriptions are not critical.
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 a specific verb ('Compute') and resource ('Pearson correlation between two metrics'), and specifies the behavior: 'over the same window, joined by date'. This clearly distinguishes it from sibling tools that analyze single metrics, compare periods, or detect anomalies.
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 clear context: use this tool when the goal is to compute the Pearson correlation between two metrics over a shared time window. It does not explicitly name alternatives or state when not to use it, but the purpose is specific enough that an agent can route correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_baselineGet metric baselineARead-onlyIdempotent
Compute "what's normal" for one metric over a window: mean, median, and standard deviation. This is the number every other analytics tool below measures against, so a wider window (60-90+ days) gives a more stable baseline than the 30-day default read_health_data uses.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes | One of steps, sleep_hours, resting_heart_rate, weight_kg, workout_minutes, mood, water_ml, heart_rate, hrv_ms. | |
| end_date | No | Last day to include, formatted YYYY-MM-DD. Defaults to today. | |
| start_date | No | First day to include, formatted YYYY-MM-DD. Defaults to 90 days before end_date. |
Output Schema
| Name | Required | Description |
|---|---|---|
| range | Yes | |
| metric | Yes | |
| baseline | Yes | |
| evidence | Yes | Coverage/quality of the data a single-metric analytical result is based on. See evidence.build_evidence for how each field is computed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and idempotentHint, and the description adds meaningful behavioral context beyond those: a privacy note explaining that returned data becomes part of the conversation and may reach a cloud model. This is valuable, non-obvious behavior that an agent should know before invoking the 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?
The description is front-loaded with the core computation, then gives window-selection guidance, then closes with an important privacy disclosure. It is slightly longer than minimal, but the privacy note earns its place given the sensitivity of the data, and no sentence is redundant.
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?
For a simple read-only tool with full schema coverage, an output schema, and clear annotations, the description covers what it computes, how to choose a useful window, and the critical privacy caveat. Nothing an agent needs in order to call it correctly or decide whether to use it 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 100%, so the baseline is 3, but the description goes further by advising that a wider window (60-90+ days) yields a more stable baseline. That adds practical meaning for setting start_date/end_date beyond the schema's structural defaults.
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 a specific verb ('Compute') and names exactly what the tool returns: a baseline for one metric over a window, with mean, median, and standard deviation. It also distinguishes the tool from siblings by calling it the number every other analytics tool measures against.
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 gives clear context for when to use this tool, especially the note that a wider 60-90+ day window produces a more stable baseline than read_health_data's 30-day default. It does not explicitly list when-not-to-use cases or detailed exclusions, but the alternative is named and contrasted.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_metric_historyGet metric historyARead-onlyIdempotent
Read one metric's day-by-day values, without the other six metrics read_health_data always includes. Use this when you only care about a single metric (e.g. before calling get_baseline or calculate_metric_trend yourself) and don't need the full multi-metric payload.
Use this tool when:
the user wants one specific metric's history/trend over time (e.g. "show my weight over the last 30 days", "how has resting heart rate changed this month").
Do not use this tool when:
the request is broad/general, across multiple metrics at once -> use
read_health_datainstead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes | One of steps, sleep_hours, resting_heart_rate, weight_kg, workout_minutes, mood, water_ml, heart_rate, hrv_ms. Rejected if configured as private via HEALTH_PRIVATE_FIELDS. | |
| end_date | No | Last day to include, formatted YYYY-MM-DD. Defaults to today. | |
| start_date | No | First day to include, formatted YYYY-MM-DD. Defaults to 30 days before end_date. |
Output Schema
| Name | Required | Description |
|---|---|---|
| range | Yes | |
| metric | Yes | |
| points | Yes | |
| evidence | Yes | Coverage/quality of the data a single-metric analytical result is based on. See evidence.build_evidence for how each field is computed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the operation readOnlyHint and idempotentHint, and the description adds a useful privacy/data-sharing caveat plus the fact that the result is single-metric and day-by-day. This goes beyond annotations without contradicting them.
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 organized into clear sections: purpose, when/not-to-use, and privacy, with the most important information front-loaded. It is slightly longer than necessary for a simple read tool, but every section 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?
For a read-only, idempotent tool with one required parameter and an output schema, the description covers behavior, selection criteria, alternatives, and privacy implications. There are no major gaps an agent would need to resolve.
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?
All parameters are documented in the schema (100% coverage), including formats and defaults, so the description does not need to repeat them. It adds context about selecting a single metric but does not materially improve on the schema's parameter documentation.
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 opens with 'Read one metric's day-by-day values', naming a specific verb, resource, and temporal granularity, and immediately distinguishes itself from read_health_data by noting it omits the other six metrics. This makes the tool's scope unmistakable for an agent.
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?
It gives explicit 'Use this tool when' and 'Do not use this tool when' guidance, includes concrete user-phrase examples, and names read_health_data as the alternative for broad multi-metric requests. No inference is required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_metric_provenanceBreak a metric down by sourceARead-onlyIdempotent
Show one metric's raw measurements for one day, broken down by which source reported them โ answers "which one is correct?" when e.g. an Apple Watch and a Garmin disagree on resting heart rate, instead of silently averaging two different devices into one number.
Do not use this tool when:
the user just wants a plain day-by-day history for the metric, with no need to see the per-source breakdown -> use
get_metric_historyinstead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | The day to inspect, formatted YYYY-MM-DD. | |
| metric | Yes | Name of the metric to inspect, e.g. "resting_heart_rate". |
Output Schema
| Name | Required | Description |
|---|---|---|
| date | Yes | |
| metric | Yes | |
| sources | Yes | |
| conflict | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive behavior, and the description adds meaningful context beyond that: it explains the data becomes part of the conversation and may be sent to a cloud model. This is a real behavioral disclosure about data flow and privacy that an agent could not infer from the schema or annotations.
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 well structured: purpose leads, a do-not-use block clearly distinguishes the sibling tool, and the privacy note is placed at the end. It is longer than strictly necessary because of the privacy explanation, but each paragraph earns its place and the key information is 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 the simple two-parameter input, full schema coverage, output schema presence, and complete annotations, the description covers what an agent needs to call this tool correctly. The only minor gap is that it does not spell out the exact return shape or whether source values are normalized, but the output schema covers that burden.
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 covers 100% of the two parameters, including formats and examples for `date` and `metric`. The description does not add much beyond the schema, but the terms 'one day' and 'metric' reinforce the single-day, single-metric scope. The schema does the heavy lifting, so a baseline 3 is appropriate.
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 states a concrete action and resource: 'Show one metric's raw measurements for one day, broken down by which source reported them.' It directly distinguishes the tool from the sibling `get_metric_history` by emphasizing the per-source breakdown and the 'which one is correct?' use case. The verb and scope are specific enough that an agent can tell what it is for.
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 explicitly names the alternative: 'Do not use this tool when... use `get_metric_history` instead.' It also gives a concrete triggering scenario (e.g., Apple Watch and Garmin disagreeing on resting heart rate), making both when-to-use and when-not-to-use clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recent_changesGet recent changesARead-onlyIdempotent
Scan every (non-private) metric for what's changed lately: a recent period vs. the four-times-as-long period before it (period-over-period shift), any anomalies inside the recent period, and a trend over it. The single best tool to start a "how have I been doing?" conversation with โ it does the scanning across all metrics that would otherwise take one get_baseline/detect_metric_anomalies/calculate_metric_trend call per metric.
Do not use this tool when:
the user already named a specific metric and wants the full why-bundle for it (value, baseline, anomaly flag, trend, correlated metrics) -> use
explain_metric_changeinstead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Length of the "recent" window in days (default 7). The comparison baseline is the 4x-as-long period immediately before it, so a 7-day recent window compares against the preceding 28 days. |
Output Schema
| Name | Required | Description |
|---|---|---|
| changes | Yes | |
| recent_range | Yes | |
| baseline_range | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true and idempotentHint=true, so the safety profile is covered. The description adds valuable context beyond those: it scans all non-private metrics, and it includes a privacy note that returned data becomes part of the model conversation. It doesn't cover every possible behavioral trait, but it goes well beyond the structured fields.
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 front-loaded with the core purpose and never repeats the title. Every section earns its place: purpose, when-to-use, when-not-to-use with a named alternative, and a meaningful privacy caveat. It is longer than average, but the length is justified by actionable content rather than 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?
For a read-only scanning tool with a single optional parameter and an output schema, the description covers everything needed for correct selection and invocation: what it scans, how windows are defined, when to prefer it, when to avoid it, and a privacy consideration. The output schema can carry return-format details, so their absence here is not a 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?
Schema description coverage is 100%, so the schema already fully documents the days parameter and the 4x comparison window. The description reinforces the same semantics without adding new parameter-level meaning. Baseline 3 is appropriate here because the structured schema does the heavy lifting.
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 verb ('scan'), the resource ('every non-private metric'), and the exact analysis performed (period-over-period shift, anomalies, trend). It also explicitly differentiates itself from sibling per-metric tools like explain_metric_change, so an agent can tell when this is the cross-metric overview versus a targeted analysis.
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?
It states the ideal use case: starting a 'how have I been doing?' conversation across all metrics. It also gives an explicit exclusion: if the user named a specific metric and wants the full why-bundle, use explain_metric_change instead. This gives clear routing guidance beyond what the schema or annotations provide.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
log_daily_metricLog a daily metricAIdempotent
Record one or more health metrics for a single day, creating that day's row if it doesn't already have one.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
Only the metrics you pass are written โ anything left as null is not touched, so logging just today's mood doesn't erase today's steps if they were set earlier. To undo a value logged by mistake, use clear_metric rather than trying to overwrite it with a placeholder.
Use this tool when:
the user is recording a simple day-level value for one of the nine fixed metrics below (e.g. "log my weight as 82 kg", "I walked 8,000 steps today").
Do not use this tool when:
the observation needs its own timestamp/source, or the day may have more than one reading of the same metric -> use
log_measurementinstead.it's a workout/exercise session -> use
log_workout_sessioninstead (workout_minutes here is just the daily total, not the session itself).
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | The day to log, formatted YYYY-MM-DD. | |
| mood | No | Mood rating on a 1-10 scale. | |
| steps | No | Step count for the day. 0-200,000. | |
| hrv_ms | No | Heart rate variability in milliseconds. 0-300. | |
| water_ml | No | Water intake in millilitres. 0-10,000. | |
| weight_kg | No | Body weight in kilograms. 1-500. | |
| heart_rate | No | Non-resting heart rate reading in bpm. 20-250. | |
| sleep_hours | No | Hours of sleep. 0-24. | |
| workout_minutes | No | Minutes of exercise. 0-1,440. | |
| resting_heart_rate | No | Resting heart rate in bpm. 20-250. |
Output Schema
| Name | Required | Description |
|---|---|---|
| row | Yes | |
| logged | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already provide idempotentHint=true, destructiveHint=false, and readOnlyHint=false, and the description extends these with detailed behavioral context: writes are partial-upsert, nulls are not touched, day rows are created when absent, and returned data becomes part of the conversation sent to the model. It also adds a privacy disclosure about local SQLite and cloud models. This goes well beyond what the annotations disclose.
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 more verbose than average but every sentence earns its place: the opening purpose statement is front-loaded, then the rationale behind the partial-update behavior, followed by a clear when/when-not block. The privacy note is arguably tangential to successful tool invocation, but it is valuable context that an agent collecting data should know.
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?
With 10 parameters, a shared data domain, and several near-sibling tools, the description covers all the essential context: scope, alternatives, parameter semantics, privacy exposure, and the undo path. It does this while an output schema exists, so return values are appropriately left outside the description. Nothing an agent needs to decide or invoke correctly 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?
Despite 100% schema description coverage, the description adds meaning the schema alone does not convey: 'anything left as null is not touched' explains the behavioral effect of passing incomplete parameters, and 'workout_minutes here is just the daily total, not the session itself' disambiguates a parameter that could be confused with log_workout_session. The schema contains type/range descriptions for every parameter, so this added guidance elevates the definition well above the baseline.
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 states a specific verb and resource: 'Record one or more health metrics for a single day, creating that day's row if it doesn't already have one.' It distinguishes the tool from similar siblings by explicitly scoping to day-level values and contrasting with log_measurement and log_workout_session. This is unambiguous even before reading any schema.
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 contains explicit 'Use this tool when' and 'Do not use this tool when' sections, naming the specific alternatives (log_measurement, log_workout_session) and the exact conditions under which they should be chosen. It also guides cleanup for mistaken logs to clear_metric, leaving no ambiguity about when this tool is appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
log_measurementLog a raw measurementA
Record a single raw observation โ one metric, one value, one point in time โ rather than a whole day's summary. Use this instead of log_daily_metric when the source, exact time, or the fact that there were multiple readings that day matters (e.g. three separate workouts, or a wearable's periodic heart-rate samples).
Use this tool when:
recording one timestamped observation where the exact time, source, or possibility of multiple same-day readings matters (e.g. "record my blood pressure reading from my cuff at 7am").
Do not use this tool when:
it's just a single end-of-day value for a fixed metric -> use
log_daily_metricinstead.it's a workout/exercise session -> use
log_workout_sessioninstead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| unit | No | Unit the value is in, e.g. "bpm", "kg". Optional. | |
| value | Yes | The numeric reading. | |
| metric | Yes | Name of the metric, e.g. "resting_heart_rate", "steps". Free-form โ not limited to daily_metrics' fixed columns. | |
| source | No | Where this came from, e.g. "Apple Watch", "manual". Optional. | |
| timestamp | Yes | When the observation was taken, YYYY-MM-DD or a full ISO 8601 timestamp (YYYY-MM-DDTHH:MM:SS). | |
| source_type | No | Category of source, e.g. "wearable", "manual", "app". Optional. |
Output Schema
| Name | Required | Description |
|---|---|---|
| measurement | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only supply basic flags (readOnly=false, destructive=false, idempotent=false), so the description adds value by disclosing that data becomes part of the conversation sent to the client's model and noting the local SQLite context. It could add more about duplicate handling or write confirmation, but the output schema and basic annotation profile cover the rest.
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 front-loaded with the core definition, followed by tight bulleted usage rules and a relevant privacy note. Every sentence earns its place; there is no filler or repetition of schema details.
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?
For a 6-parameter tool with a full input schema and an output schema, the description covers granularity, sibling routing, and a meaningful privacy behavior. Nothing essential is missing for an agent to select and invoke the tool 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 coverage is 100%, so the baseline is 3, but the description adds semantic meaning beyond the schema: it frames the event as a raw point-in-time observation versus a daily summary, explains that source/exact-time/multiple-same-day-readings are the deciding factors, and gives a concrete example that maps metric, source, and timestamp to a real use case.
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 states the specific verb 'record' with a clear object ('a single raw observation') and defines the exact scope as one metric, one value, one point in time. It explicitly contrasts itself with log_daily_metric and log_workout_session, so an agent can distinguish it from siblings without inspecting schemas.
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 explicit 'Use this tool when' and 'Do not use this tool when' sections, naming the exact alternative tools (log_daily_metric, log_workout_session) and the conditions that route to each. It even includes a concrete example, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
log_workout_sessionLog a workout sessionA
Record one workout as a structured event โ activity, timing, intensity, and heart-rate response โ rather than folding it into the day's workout_minutes total. Use this alongside (not instead of) log_daily_metric/log_measurement for workout_minutes: this is what lets explain_metric_change say what the workout was, not just how long it ran. A day can have more than one session; each call adds a new row.
Use this tool when:
the user describes an actual workout/exercise session (e.g. "I went running for 40 minutes", "log today's strength workout").
Do not use this tool when:
the user only wants to record the day's total exercise minutes as a single number, with no activity type/timing/intensity -> use
log_daily_metric(workout_minutes) orlog_measurementinstead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | The day the workout happened, YYYY-MM-DD. | |
| notes | No | Free-text notes, e.g. route or how it felt. Optional. | |
| source | No | Where this came from, e.g. "Apple Watch", "manual". Optional. | |
| intensity | No | One of "low", "moderate", "high". Optional. | |
| start_time | No | When it started, HH:MM (24-hour) or a full ISO timestamp. Optional. | |
| activity_type | Yes | What kind of workout, e.g. "running", "cycling", "strength". Free-form. | |
| avg_heart_rate | No | Average heart rate during the workout, bpm. Optional. | |
| max_heart_rate | No | Peak heart rate during the workout, bpm. Optional. | |
| duration_minutes | Yes | How long it lasted, in minutes. |
Output Schema
| Name | Required | Description |
|---|---|---|
| session | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=false, idempotentHint=false, etc., but the description adds important behavior: 'each call adds a new row' clarifies non-idempotence specifics, and the privacy note about data becoming part of the conversation sent to the model is unique contextual disclosure. No contradictions with annotations.
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 front-loaded with a strong first sentence, followed by structured usage guidance and a privacy note. It is longer than the minimal two-sentence example, but every section contributes value given the tool's complexity and need to disambiguate from siblings. The explicitly labeled sections compensate for length.
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 description is complete enough for a 9-parameter tool with 100% schema coverage and an output schema. It covers when to use, when not, privacy implications, and multi-session behavior. No significant context is missing for an agent to invoke the tool 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?
Input schema coverage is 100%, so the schema fully documents each of the 9 parameters. The description adds a high-level grouping ('activity, timing, intensity, heart-rate response') but does not provide parameter-level semantics beyond the schema. Baseline of 3 applies since schema does the heavy lifting.
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 states a specific action and resource: 'Record one workout as a structured event โ activity, timing, intensity, and heart-rate response.' It explicitly contrasts with log_daily_metric/log_measurement for workout_minutes and explains the relationship to explain_metric_change, distinguishing it from siblings without the need to open schemas.
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?
Provides explicit 'Use this tool when' and 'Do not use this tool when' sections with concrete examples ('I went running for 40 minutes') and names the exact alternatives (log_daily_metric / log_measurement) for the excluded cases. This goes beyond mere context to actionable selection criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_health_dataRead health dataARead-onlyIdempotent
Read daily health metrics from the local database: steps, sleep hours, resting heart rate, weight (kg), workout minutes, mood, and water intake (ml).
Use this tool when:
the request is broad/general, across multiple metrics at once (e.g. "what health data do I have?", "overview of this week").
Do not use this tool when:
the user wants one specific metric's history/trend over time -> use
get_metric_historyinstead.the user wants raw/individual measurement rows (timestamp, source) -> use
read_measurementsinstead.the user wants workout sessions specifically -> use
read_workout_sessionsinstead.the user is asking why something changed, or wants a trend, anomaly, comparison, or correlation -> use
explain_metric_change(one metric, one date) orget_recent_changes(scan across all metrics) instead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| end_date | No | Last day to include, formatted YYYY-MM-DD. Defaults to today. | |
| start_date | No | First day to include, formatted YYYY-MM-DD. Defaults to 30 days before end_date. Ranges over ~10 years are rejected. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| range | Yes | |
| summary | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and idempotentHint, so the safety profile is covered. The description adds valuable context beyond annotations with the privacy note about data becoming part of the conversation sent to the client's model, and clarifies that the underlying storage is a local SQLite file. Minor missing details like return shape are handled by the output schema.
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 front-loaded with the core behavior, then uses clear use/do-not-use sections, then a privacy caveat. Every sentence earns its place, and the routing content is dense but scannable. The length is justified given the large sibling set.
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?
For a read-only list tool with an output schema, the description covers purpose, metric scope, parameter behavior via schema, sibling routing, and the privacy implications of the returned data. There are no significant gaps an agent would need to fill before calling it 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 100%, and both start_date and end_date already have clear format, default, and range-rejection documentation. The description does not add parameter-level detail, but it also does not need to since the schema fully explains them. Baseline 3 is appropriate.
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 opens with a specific verb-resource pairing: 'Read daily health metrics from the local database,' and enumerates the exact metrics included. It also distinguishes itself from siblings by emphasizing broad/general access, and the do-not-use list names the precise alternatives. This leaves no ambiguity about what the tool does.
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 explicit use-when criteria ('broad/general, across multiple metrics at once') and a thorough do-not-use section routing to specific sibling tools like get_metric_history, read_measurements, and explain_metric_change. This is model-actionable guidance that prevents selecting the wrong tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_measurementsRead raw measurementsARead-onlyIdempotent
Read individual measurement rows (not the daily_metrics aggregate), most recent first. Use this to see exactly when and where each reading came from, rather than just a day's summarized value.
Use this tool when:
the user wants raw/individual observations (e.g. "what measurements have I recorded?"), including their timestamp or source.
Do not use this tool when:
the user wants a broad, multi-metric overview -> use
read_health_datainstead.the user wants one metric's day-by-day history -> use
get_metric_historyinstead.the user wants workout sessions -> use
read_workout_sessionsinstead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum rows to return (default 200). | |
| metric | No | Only return this metric. Omit for all metrics. | |
| source | No | Only return rows from this source, e.g. "Apple Watch". Omit for all sources. | |
| end_date | No | Only return rows on/before this date (YYYY-MM-DD). Omit for no upper bound. | |
| start_date | No | Only return rows on/after this date (YYYY-MM-DD). Omit for no lower bound. |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| measurements | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool readOnlyHint=true, idempotentHint=true, and destructiveHint=false, and the description adds meaningful behavioral context beyond that: results are ordered most recent first, the tool returns raw rows rather than aggregates, and the privacy note discloses that returned data becomes part of the conversation sent to the model. No contradiction with annotations.
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 front-loaded with the core purpose, then structured into clear usage and exclusions lists, followed by a well-scoped privacy note. Every sentence earns its place; the formatting makes it easy for an agent to scan for the decision-relevant information.
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?
For a read-only, optional-parameter tool with a rich output schema and full schema coverage, the description provides everything an agent needs: what the tool returns, ordering behavior, when to prefer alternatives, and privacy implications. There is no meaningful gap in operational or decision context.
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 100%, so the schema fully documents all five optional parameters. The description adds only indirect context (e.g., that results include timestamps and sources) but does not need to explain parameter syntax or semantics since the schema already handles that burden. Baseline of 3 is appropriate.
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 a specific verb ('Read') with a precise resource ('individual measurement rows'), and immediately distinguishes the tool from the daily_metrics aggregate. It also names the data orientation (most recent first) and what kind of detail it exposes (timestamp/source), making its purpose unambiguous and distinct from sibling 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 explicitly states when to use the tool with a concrete user-intent example, and provides a dedicated 'Do not use this tool when' section that names three sibling alternatives (read_health_data, get_metric_history, read_workout_sessions). This gives an agent clear routing guidance with no inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_workout_sessionsRead workout sessionsARead-onlyIdempotent
Read individual workout sessions (not the daily_metrics workout_minutes total), most recent day first. Use this to see what each workout actually was โ activity, timing, intensity, heart rate โ rather than just a day's summed minutes.
Use this tool when:
the user asks about workouts/exercise sessions specifically (e.g. "what workouts did I do this week?", "show my recent gym sessions").
Do not use this tool when:
the user just wants the daily workout_minutes total, not individual sessions -> use
read_health_dataorget_metric_historyinstead.
Privacy note: this server and its SQLite file are entirely local, but the data returned by this tool becomes part of the conversation sent to whatever model the calling client is configured with. If that model runs in the cloud rather than on your machine, treat this the same as pasting the data into a chat with that provider.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum rows to return (default 200). | |
| end_date | No | Only return sessions on/before this date (YYYY-MM-DD). Omit for no upper bound. | |
| start_date | No | Only return sessions on/after this date (YYYY-MM-DD). Omit for no lower bound. | |
| activity_type | No | Only return sessions of this activity type. Omit for all types. |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| sessions | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnly, idempotent, non-destructive), the description adds genuinely useful behavioral context: ordering is 'most recent day first,' the tool returns session-level detail rather than daily totals, and it discloses a privacy implication about data becoming part of the conversation. These are meaningful traits not captured by the annotations.
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 front-loaded with the core definition, followed by structured usage guidance and a relevant privacy note. Every section earns its place: the first sentence states the tool's scope, the bullets give actionable routing rules, and the privacy note is important for local-vs-cloud model contexts. No filler is present.
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 rich annotations, full parameter schema coverage, and presence of an output schema, the description supplies all remaining context an agent needs: when to use it, when not to, how results are ordered, and what data is returned. The alternative tools are explicitly named, so the surrounding tool ecosystem is adequately addressed.
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 100%, so the schema already documents limit, start_date, end_date, and activity_type fully. The description reinforces the overall intent but does not add parameter-specific detail beyond what the input schema provides, matching the baseline for full schema coverage.
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 opens with a precise verb and resource: 'Read individual workout sessions,' and immediately disambiguates from the daily_metrics workout_minutes total. It also enumerates the meaningful fields returned (activity, timing, intensity, heart rate) and contrast with daily aggregates, making the tool's purpose unmistakable.
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 explicit 'Use this tool when' and 'Do not use this tool when' sections with concrete example queries and names the alternatives (read_health_data, get_metric_history). An agent can confidently route between this tool and its siblings without further inference.
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.
7 tool updates
- Changed
calculate_metric_trend2 fields changed- added
Output schema / properties / evidenceAdded value: +{ + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "metric", - "range", - "trend" -]New value: +[ + "metric", + "range", + "trend", + "evidence" +]
- Changed
compare_metric_periods3 fields changed- added
Output schema / properties / period_a_evidenceAdded value: +{ + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" +} - added
Output schema / properties / period_b_evidenceAdded value: +{ + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "metric", - "period_a", - "period_b", - "period_a_stats", - "period_b_stats" -]New value: +[ + "metric", + "period_a", + "period_b", + "period_a_stats", + "period_b_stats", + "period_a_evidence", + "period_b_evidence" +]
- Changed
detect_metric_anomalies2 fields changed- added
Output schema / properties / evidenceAdded value: +{ + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "metric", - "range", - "threshold", - "anomalies" -]New value: +[ + "metric", + "range", + "threshold", + "anomalies", + "evidence" +]
- Changed
explain_metric_change5 fields changed- added
Output schema / properties / baseline_evidenceAdded value: +{ + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" +} - added
Output schema / properties / correlated_metrics / items / properties / evidence_aAdded value: +{ + "anyOf": [ + { + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / correlated_metrics / items / properties / evidence_bAdded value: +{ + "anyOf": [ + { + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / trend_evidenceAdded value: +{ + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "metric", - "date", - "baseline_range", - "baseline", - "is_anomaly", - "trend", - "correlated_metrics", - "narrative_facts" -]New value: +[ + "metric", + "date", + "baseline_range", + "baseline", + "is_anomaly", + "trend", + "correlated_metrics", + "narrative_facts", + "baseline_evidence", + "trend_evidence" +]
- Changed
find_metric_correlation2 fields changed- added
Output schema / properties / evidence_aAdded value: +{ + "anyOf": [ + { + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / evidence_bAdded value: +{ + "anyOf": [ + { + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "default": null +}
- Changed
get_baseline2 fields changed- added
Output schema / properties / evidenceAdded value: +{ + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "metric", - "range", - "baseline" -]New value: +[ + "metric", + "range", + "baseline", + "evidence" +]
- Changed
get_metric_history2 fields changed- added
Output schema / properties / evidenceAdded value: +{ + "description": "Coverage/quality of the data a single-metric analytical result is\nbased on. See evidence.build_evidence for how each field is computed.", + "properties": { + "confidence": { + "type": "string" + }, + "coverage_ratio": { + "type": "number" + }, + "expected_days": { + "type": "integer" + }, + "freshness_days": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "gaps": { + "items": { + "properties": { + "days": { + "type": "integer" + }, + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "days" + ], + "type": "object" + }, + "type": "array" + }, + "measurement_count": { + "type": "integer" + }, + "missing_days": { + "type": "integer" + }, + "observed_days": { + "type": "integer" + }, + "observed_end": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "observed_start": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "recent_gap_days": { + "type": "integer" + }, + "requested_end": { + "type": "string" + }, + "requested_start": { + "type": "string" + } + }, + "required": [ + "requested_start", + "requested_end", + "expected_days", + "observed_days", + "coverage_ratio", + "missing_days", + "measurement_count", + "gaps", + "recent_gap_days", + "confidence" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "metric", - "range", - "points" -]New value: +[ + "metric", + "range", + "points", + "evidence" +]
27 tool updates
v1.0.16- Added
aggregate_measurements - Added
calculate_metric_trend - Removed
clear_daily_metric - Removed
clear_daily_metrics_range - Added
clear_metric - Added
compare_metric_periods - Removed
delete_measurement - Removed
delete_workout - Added
detect_metric_anomalies - Added
explain_metric_change - Added
export_health_data_csv - Added
find_metric_correlation - Added
get_baseline - Added
get_metric_history - Added
get_metric_provenance - Added
get_recent_changes - Changed
log_daily_metric15 fields changed- added
Input schema / properties / date / descriptionAdded value: +"The day to log, formatted YYYY-MM-DD." - added
Input schema / properties / heart_rateAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Non-resting heart rate reading in bpm. 20-250." +} - added
Input schema / properties / hrv_msAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Heart rate variability in milliseconds. 0-300." +} - added
Input schema / properties / moodAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Mood rating on a 1-10 scale." +} - added
Input schema / properties / resting_heart_rate / descriptionAdded value: +"Resting heart rate in bpm. 20-250." - added
Input schema / properties / sleep_hours / descriptionAdded value: +"Hours of sleep. 0-24." - added
Input schema / properties / steps / descriptionAdded value: +"Step count for the day. 0-200,000." - added
Input schema / properties / water_mlAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Water intake in millilitres. 0-10,000." +} - added
Input schema / properties / weight_kgAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Body weight in kilograms. 1-500." +} - added
Input schema / properties / workout_minutesAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Minutes of exercise. 0-1,440." +} - added
Output schema / properties / loggedAdded value: +{ + "additionalProperties": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ] + }, + "type": "object" +} - removed
Output schema / properties / resultRemoved value: -{ - "type": "string" -} - added
Output schema / properties / rowAdded value: +{ + "properties": { + "date": { + "type": "string" + }, + "heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "hrv_ms": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "mood": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "resting_heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "sleep_hours": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "steps": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "water_ml": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "weight_kg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "workout_minutes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "date" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "logged", + "row" +] - removed
Output schema / x-fastmcp-wrap-resultRemoved value: -true
- Changed
log_measurement14 fields changed- removed
Input schema / properties / dateRemoved value: -{ - "type": "string" -} - added
Input schema / properties / metricAdded value: +{ + "description": "Name of the metric, e.g. \"resting_heart_rate\", \"steps\".\nFree-form โ not limited to daily_metrics' fixed columns.", + "type": "string" +} - removed
Input schema / properties / metric_nameRemoved value: -{ - "type": "string" -} - removed
Input schema / properties / notesRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null -} - added
Input schema / properties / sourceAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Where this came from, e.g. \"Apple Watch\", \"manual\". Optional." +} - added
Input schema / properties / source_typeAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Category of source, e.g. \"wearable\", \"manual\", \"app\". Optional." +} - added
Input schema / properties / timestampAdded value: +{ + "description": "When the observation was taken, YYYY-MM-DD or a full\nISO 8601 timestamp (YYYY-MM-DDTHH:MM:SS).", + "type": "string" +} - added
Input schema / properties / unit / descriptionAdded value: +"Unit the value is in, e.g. \"bpm\", \"kg\". Optional." - added
Input schema / properties / value / descriptionAdded value: +"The numeric reading." - changed
Input schema / requiredPrevious value: -[ - "date", - "metric_name", - "value" -]New value: +[ + "timestamp", + "metric", + "value" +] - added
Output schema / properties / measurementAdded value: +{ + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "metric": { + "type": "string" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "timestamp": { + "type": "string" + }, + "unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "value": { + "type": "number" + } + }, + "required": [ + "id", + "timestamp", + "metric", + "value", + "created_at" + ], + "type": "object" +} - removed
Output schema / properties / resultRemoved value: -{ - "type": "string" -} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "measurement" +] - removed
Output schema / x-fastmcp-wrap-resultRemoved value: -true
- Removed
log_workout - Added
log_workout_session - Removed
read_finance_data - Changed
read_health_data9 fields changed- added
Input schema / properties / end_date / descriptionAdded value: +"Last day to include, formatted YYYY-MM-DD. Defaults to today." - added
Input schema / properties / start_date / descriptionAdded value: +"First day to include, formatted YYYY-MM-DD.\nDefaults to 30 days before end_date. Ranges over ~10 years are rejected." - added
Output schema / properties / rangeAdded value: +{ + "properties": { + "end_date": { + "type": "string" + }, + "start_date": { + "type": "string" + } + }, + "required": [ + "start_date", + "end_date" + ], + "type": "object" +} - removed
Output schema / properties / resultRemoved value: -{ - "type": "string" -} - added
Output schema / properties / rowsAdded value: +{ + "items": { + "properties": { + "date": { + "type": "string" + }, + "heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "hrv_ms": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "mood": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "resting_heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "sleep_hours": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "steps": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "water_ml": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "weight_kg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "workout_minutes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "date" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / summaryAdded value: +{ + "properties": { + "days_with_data": { + "type": "integer" + }, + "heart_rate": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "hrv_ms": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "mood": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "resting_heart_rate": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "sleep_hours": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "steps": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "water_ml": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "weight_kg": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "workout_minutes": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + } + }, + "required": [ + "days_with_data", + "steps", + "sleep_hours", + "resting_heart_rate", + "weight_kg", + "workout_minutes", + "mood", + "water_ml", + "heart_rate", + "hrv_ms" + ], + "type": "object" +} - added
Output schema / properties / truncatedAdded value: +{ + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "range", + "rows", + "truncated", + "summary" +] - removed
Output schema / x-fastmcp-wrap-resultRemoved value: -true
- Added
read_measurements - Added
read_workout_sessions - Removed
update_daily_metric - Removed
update_measurement - Removed
update_workout
27 tool updates
v1.0.15- Removed
aggregate_measurements - Removed
calculate_metric_trend - Added
clear_daily_metric - Added
clear_daily_metrics_range - Removed
clear_metric - Removed
compare_metric_periods - Added
delete_measurement - Added
delete_workout - Removed
detect_metric_anomalies - Removed
explain_metric_change - Removed
export_health_data_csv - Removed
find_metric_correlation - Removed
get_baseline - Removed
get_metric_history - Removed
get_metric_provenance - Removed
get_recent_changes - Changed
log_daily_metric15 fields changed- removed
Input schema / properties / date / descriptionRemoved value: -"The day to log, formatted YYYY-MM-DD." - removed
Input schema / properties / heart_rateRemoved value: -{ - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Non-resting heart rate reading in bpm. 20-250." -} - removed
Input schema / properties / hrv_msRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Heart rate variability in milliseconds. 0-300." -} - removed
Input schema / properties / moodRemoved value: -{ - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Mood rating on a 1-10 scale." -} - removed
Input schema / properties / resting_heart_rate / descriptionRemoved value: -"Resting heart rate in bpm. 20-250." - removed
Input schema / properties / sleep_hours / descriptionRemoved value: -"Hours of sleep. 0-24." - removed
Input schema / properties / steps / descriptionRemoved value: -"Step count for the day. 0-200,000." - removed
Input schema / properties / water_mlRemoved value: -{ - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Water intake in millilitres. 0-10,000." -} - removed
Input schema / properties / weight_kgRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Body weight in kilograms. 1-500." -} - removed
Input schema / properties / workout_minutesRemoved value: -{ - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Minutes of exercise. 0-1,440." -} - removed
Output schema / properties / loggedRemoved value: -{ - "additionalProperties": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "number" - } - ] - }, - "type": "object" -} - added
Output schema / properties / resultAdded value: +{ + "type": "string" +} - removed
Output schema / properties / rowRemoved value: -{ - "properties": { - "date": { - "type": "string" - }, - "heart_rate": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "hrv_ms": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "mood": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "resting_heart_rate": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "sleep_hours": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "steps": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "water_ml": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "weight_kg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "workout_minutes": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "date" - ], - "type": "object" -} - changed
Output schema / requiredPrevious value: -[ - "logged", - "row" -]New value: +[ + "result" +] - added
Output schema / x-fastmcp-wrap-resultAdded value: +true
- Changed
log_measurement14 fields changed- added
Input schema / properties / dateAdded value: +{ + "type": "string" +} - removed
Input schema / properties / metricRemoved value: -{ - "description": "Name of the metric, e.g. \"resting_heart_rate\", \"steps\".\nFree-form โ not limited to daily_metrics' fixed columns.", - "type": "string" -} - added
Input schema / properties / metric_nameAdded value: +{ + "type": "string" +} - added
Input schema / properties / notesAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null +} - removed
Input schema / properties / sourceRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Where this came from, e.g. \"Apple Watch\", \"manual\". Optional." -} - removed
Input schema / properties / source_typeRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Category of source, e.g. \"wearable\", \"manual\", \"app\". Optional." -} - removed
Input schema / properties / timestampRemoved value: -{ - "description": "When the observation was taken, YYYY-MM-DD or a full\nISO 8601 timestamp (YYYY-MM-DDTHH:MM:SS).", - "type": "string" -} - removed
Input schema / properties / unit / descriptionRemoved value: -"Unit the value is in, e.g. \"bpm\", \"kg\". Optional." - removed
Input schema / properties / value / descriptionRemoved value: -"The numeric reading." - changed
Input schema / requiredPrevious value: -[ - "timestamp", - "metric", - "value" -]New value: +[ + "date", + "metric_name", + "value" +] - removed
Output schema / properties / measurementRemoved value: -{ - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "metric": { - "type": "string" - }, - "source": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "source_type": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "timestamp": { - "type": "string" - }, - "unit": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null - }, - "value": { - "type": "number" - } - }, - "required": [ - "id", - "timestamp", - "metric", - "value", - "created_at" - ], - "type": "object" -} - added
Output schema / properties / resultAdded value: +{ + "type": "string" +} - changed
Output schema / requiredPrevious value: -[ - "measurement" -]New value: +[ + "result" +] - added
Output schema / x-fastmcp-wrap-resultAdded value: +true
- Added
log_workout - Removed
log_workout_session - Added
read_finance_data - Changed
read_health_data9 fields changed- removed
Input schema / properties / end_date / descriptionRemoved value: -"Last day to include, formatted YYYY-MM-DD. Defaults to today." - removed
Input schema / properties / start_date / descriptionRemoved value: -"First day to include, formatted YYYY-MM-DD.\nDefaults to 30 days before end_date. Ranges over ~10 years are rejected." - removed
Output schema / properties / rangeRemoved value: -{ - "properties": { - "end_date": { - "type": "string" - }, - "start_date": { - "type": "string" - } - }, - "required": [ - "start_date", - "end_date" - ], - "type": "object" -} - added
Output schema / properties / resultAdded value: +{ + "type": "string" +} - removed
Output schema / properties / rowsRemoved value: -{ - "items": { - "properties": { - "date": { - "type": "string" - }, - "heart_rate": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "hrv_ms": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "mood": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "resting_heart_rate": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "sleep_hours": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "steps": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "water_ml": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "weight_kg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "workout_minutes": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "date" - ], - "type": "object" - }, - "type": "array" -} - removed
Output schema / properties / summaryRemoved value: -{ - "properties": { - "days_with_data": { - "type": "integer" - }, - "heart_rate": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - }, - "hrv_ms": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - }, - "mood": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - }, - "resting_heart_rate": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - }, - "sleep_hours": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - }, - "steps": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - }, - "water_ml": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - }, - "weight_kg": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - }, - "workout_minutes": { - "properties": { - "avg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "max": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "min": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "type": "object" - } - }, - "required": [ - "days_with_data", - "steps", - "sleep_hours", - "resting_heart_rate", - "weight_kg", - "workout_minutes", - "mood", - "water_ml", - "heart_rate", - "hrv_ms" - ], - "type": "object" -} - removed
Output schema / properties / truncatedRemoved value: -{ - "type": "boolean" -} - changed
Output schema / requiredPrevious value: -[ - "range", - "rows", - "truncated", - "summary" -]New value: +[ + "result" +] - added
Output schema / x-fastmcp-wrap-resultAdded value: +true
- Removed
read_measurements - Removed
read_workout_sessions - Added
update_daily_metric - Added
update_measurement - Added
update_workout
3 tool updates
v0.3.0- Changed
explain_metric_change1 field changed- added
Output schema / properties / sessionsAdded value: +{ + "default": [], + "items": { + "properties": { + "activity_type": { + "type": "string" + }, + "avg_heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "created_at": { + "type": "string" + }, + "date": { + "type": "string" + }, + "duration_minutes": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "intensity": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "max_heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "notes": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + }, + "start_time": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "date", + "activity_type", + "duration_minutes", + "created_at" + ], + "type": "object" + }, + "type": "array" +}
- Added
log_workout_session - Added
read_workout_sessions
16 tool updates
v1.0.11- Added
aggregate_measurements - Added
calculate_metric_trend - Changed
clear_metric2 fields changed- changed
Input schema / properties / field / descriptionPrevious value: -"Which metric to blank out. One of: steps, sleep_hours,\nresting_heart_rate, weight_kg, workout_minutes, mood, water_ml."New value: +"Which metric to blank out. One of: steps, sleep_hours,\nresting_heart_rate, weight_kg, workout_minutes, mood, water_ml, heart_rate, hrv_ms." - changed
Output schema / properties / row / anyOfPrevious value: -[ - { - "properties": { - "date": { - "type": "string" - }, - "mood": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "resting_heart_rate": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "sleep_hours": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "steps": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "water_ml": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - }, - "weight_kg": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null - }, - "workout_minutes": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null - } - }, - "required": [ - "date" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "properties": { + "date": { + "type": "string" + }, + "heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "hrv_ms": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "mood": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "resting_heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "sleep_hours": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "steps": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "water_ml": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "weight_kg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "workout_minutes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "date" + ], + "type": "object" + }, + { + "type": "null" + } +]
- Added
compare_metric_periods - Added
detect_metric_anomalies - Added
explain_metric_change - Added
export_health_data_csv - Added
find_metric_correlation - Added
get_baseline - Added
get_metric_history - Added
get_metric_provenance - Added
get_recent_changes - Changed
log_daily_metric4 fields changed- added
Input schema / properties / heart_rateAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Non-resting heart rate reading in bpm. 20-250." +} - added
Input schema / properties / hrv_msAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Heart rate variability in milliseconds. 0-300." +} - added
Output schema / properties / row / properties / heart_rateAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / row / properties / hrv_msAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null +}
- Added
log_measurement - Changed
read_health_data5 fields changed- added
Output schema / properties / rows / items / properties / heart_rateAdded value: +{ + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / rows / items / properties / hrv_msAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / summary / properties / heart_rateAdded value: +{ + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" +} - added
Output schema / properties / summary / properties / hrv_msAdded value: +{ + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" +} - changed
Output schema / properties / summary / requiredPrevious value: -[ - "days_with_data", - "steps", - "sleep_hours", - "resting_heart_rate", - "weight_kg", - "workout_minutes", - "mood", - "water_ml" -]New value: +[ + "days_with_data", + "steps", + "sleep_hours", + "resting_heart_rate", + "weight_kg", + "workout_minutes", + "mood", + "water_ml", + "heart_rate", + "hrv_ms" +]
- Added
read_measurements
3 tool updates
v1.0.8- Changed
clear_metric6 fields changed- added
Output schema / properties / clearedAdded value: +{ + "type": "string" +} - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null +} - removed
Output schema / properties / resultRemoved value: -{ - "type": "string" -} - added
Output schema / properties / rowAdded value: +{ + "anyOf": [ + { + "properties": { + "date": { + "type": "string" + }, + "mood": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "resting_heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "sleep_hours": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "steps": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "water_ml": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "weight_kg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "workout_minutes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "date" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "default": null +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "cleared" +] - removed
Output schema / x-fastmcp-wrap-resultRemoved value: -true
- Changed
log_daily_metric5 fields changed- added
Output schema / properties / loggedAdded value: +{ + "additionalProperties": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ] + }, + "type": "object" +} - removed
Output schema / properties / resultRemoved value: -{ - "type": "string" -} - added
Output schema / properties / rowAdded value: +{ + "properties": { + "date": { + "type": "string" + }, + "mood": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "resting_heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "sleep_hours": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "steps": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "water_ml": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "weight_kg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "workout_minutes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "date" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "logged", + "row" +] - removed
Output schema / x-fastmcp-wrap-resultRemoved value: -true
- Changed
read_health_data7 fields changed- added
Output schema / properties / rangeAdded value: +{ + "properties": { + "end_date": { + "type": "string" + }, + "start_date": { + "type": "string" + } + }, + "required": [ + "start_date", + "end_date" + ], + "type": "object" +} - removed
Output schema / properties / resultRemoved value: -{ - "type": "string" -} - added
Output schema / properties / rowsAdded value: +{ + "items": { + "properties": { + "date": { + "type": "string" + }, + "mood": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "resting_heart_rate": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "sleep_hours": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "steps": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "water_ml": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + }, + "weight_kg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "workout_minutes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "date" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / summaryAdded value: +{ + "properties": { + "days_with_data": { + "type": "integer" + }, + "mood": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "resting_heart_rate": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "sleep_hours": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "steps": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "water_ml": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "weight_kg": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + }, + "workout_minutes": { + "properties": { + "avg": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "max": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + }, + "min": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "type": "object" + } + }, + "required": [ + "days_with_data", + "steps", + "sleep_hours", + "resting_heart_rate", + "weight_kg", + "workout_minutes", + "mood", + "water_ml" + ], + "type": "object" +} - added
Output schema / properties / truncatedAdded value: +{ + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "range", + "rows", + "truncated", + "summary" +] - removed
Output schema / x-fastmcp-wrap-resultRemoved value: -true
2 tool updates
v1.0.4- Added
clear_metric - Added
log_daily_metric
2 tool updates
v1.0.3- Removed
read_finance_data - Changed
read_health_data1 field changed- changed
Input schema / properties / start_date / descriptionPrevious value: -"First day to include, formatted YYYY-MM-DD.\n Defaults to 30 days before end_date."New value: +"First day to include, formatted YYYY-MM-DD.\nDefaults to 30 days before end_date. Ranges over ~10 years are rejected."
1 tool update
v1.0.1- Changed
read_finance_data1 field changed- changed
Input schema / properties / category / descriptionPrevious value: -"Optional category name to filter to (case-insensitive,\n exact match โ e.g. \"Groceries\"). Omit to include all categories."New value: +"Optional category name to filter to (case-insensitive,\n exact match โ e.g. \"Groceries\"). A category with no matching\n rows returns an empty \"transactions\" list, not an error โ this\n usually means a typo or a category that isn't in the ledger.\n Omit to include all categories."
2 tool updates
v1.0.0- First observed
read_finance_data - First observed
read_health_data
TDQS
Scored across 18 tools
Each tool has a distinct purpose, and the 'Do not use this tool when' cross-references make boundaries unusually explicit. The analytics cluster (get_baseline, detect_metric_anomalies, calculate_metric_trend, compare_metric_periods, find_metric_correlation, get_recent_changes, explain_metric_change) still requires careful reading to select correctly.
All names follow a readable snake_case verb_noun pattern and clearly describe their action. The main inconsistency is using read_* for some retrieval tools and get_* for others, but there is no chaotic mixing of styles.
18 tools is slightly above the ideal 3-15 range, but the count is justified by the server's scope: day-level metrics, raw measurements, workout sessions, analytics, and export. No tool feels redundant or like filler.
The surface covers logging, clearing, reading, aggregation, analytics, and export for daily metrics, raw measurements, and workout sessions. The main gap is the absence of delete/update operations for raw measurements and workout sessions, since clear_metric only removes day-level metric values.
Maintenance
Related MCP Connectors
- Era ContextOAuthapp.era
Personal finance, bank account, and shared memory connector for Claude, ChatGPT, Gemini Spark & more
Query 40 databases from Claude, ChatGPT, or Cursor โ on any device. Read-only, encrypted, audited.
Your personal data for AI โ Telegram, bank, courses, Zoom & more, scoped to you.
Ask data questions in natural language. Get SQL, insights, and charts from your databases.
Related MCP Servers
- AlicenseAqualityAmaintenanceAn MCP server that allows users to query and analyze their Apple Health data using SQL and natural language, utilizing DuckDB for fast and efficient health data analysis.2371 npm567MIT
- AlicenseNot gradedqualityCmaintenanceLoads Apple Health export data into a local SQLite database and exposes tools to query health metrics and workout records via natural language.3MIT
- AlicenseNot gradedqualityDmaintenanceEnables querying personal data synced from services like Lunch Money and Strava using SQL via Claude.6 npm1MIT
- AlicenseBqualityCmaintenanceEnables AI agents to interact with local SQLite databases with full CRUD, schema introspection, foreign key relations, generated columns, and multi-format import/export (CSV, JSON, XLSX) through natural language.269 npmMIT