Quantified Self MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| HEALTH_DB_PATH | No | Optional path to the SQLite health database. If not set, the server uses its default database path. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| logging | {} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| read_health_dataA | 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:
Do not use this tool when:
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. |
| export_health_data_csvA | 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. |
| log_daily_metricA | 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:
Do not use this tool when:
|
| clear_metricA | 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. Clears everything recorded for that metric/day — including individual log_measurement readings or imported rows, not just a value log_daily_metric wrote directly — so the metric genuinely goes back to "nothing recorded" rather than falling back to a blended value from whatever else is left. 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. |
| log_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:
Do not use this tool when:
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. |
| read_measurementsA | 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:
Do not use this tool when:
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. |
| aggregate_measurementsA | Preview what one day's raw measurements would roll up to if a conflicting metric were resolved using source_priority, alongside that day's actual current daily_metrics values. daily_metrics is a database-maintained projection (see db/schema.sql): every log_measurement/import automatically keeps it in sync the moment it's written, using each metric's fixed aggregation method (sum/mean/last — see aggregation_rules, or get_baseline's "method" field). When a metric has measurements from more than one source on a day, the projection uses only one source's observations — never a blend of devices. It takes the highest-ranked present source in the stored source priority list (manual log_daily_metric entries rank first by default); if none is ranked, the source that observed the most hours of that day, then the one with the latest observation, then by name. This tool writes nothing: "aggregated" previews what the source_priority you pass would produce; "row" is the real, currently-stored value, which differs from "aggregated" whenever the stored priority differs from the one you pass. 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. 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. |
| get_metric_provenanceA | 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:
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. |
| log_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:
Do not use this tool when:
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. |
| read_workout_sessionsA | 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:
Do not use this tool when:
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. |
| get_data_statusA | Report how current and complete the database is: latest data date, coverage window, gaps, last successful import, and an overall status of CURRENT, STALE, INCOMPLETE, NO_DATA or IMPORT_FAILED. Check this before drawing conclusions from any analysis, and say so when the status is anything other than CURRENT. Use this tool when:
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. |
| get_import_statusA | List the most recent import runs, newest first: which importer, the source file's name and SHA-256, whether the run succeeded, failed or was interrupted, and how many rows were loaded, skipped and written. Use this tool when:
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. |
| get_metric_historyA | 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:
Do not use this tool when:
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. |
| get_baselineA | 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. Use this tool when:
Do not use this tool when:
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. |
| detect_metric_anomaliesA | 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. Use this tool when:
Do not use this tool when:
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. |
| calculate_metric_trendA | 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"). Use this tool when:
Do not use this tool when:
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. |
| compare_metric_periodsA | 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. Use this tool when:
Do not use this tool when:
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. |
| find_metric_correlationA | 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. Use this tool when:
Do not use this tool when:
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. |
| get_recent_changesA | 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:
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. |
| explain_metric_changeA | 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:
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. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| Metric schema | The full set of metrics this server tracks, with each one's valid range (see logic.METRIC_BOUNDS) and whether it's currently configured as private (see HEALTH_PRIVATE_FIELDS). Read this to see accepted ranges up front, instead of discovering them one at a time from log_daily_metric's invalid_metric_value errors. |
TDQS
Scored across 20 tools
Every tool carries explicit 'use this when' and 'do not use this when' guidance that routes overlapping analytics tools to one another, so the boundaries between get_baseline, detect_metric_anomalies, calculate_metric_trend, compare_metric_periods, get_recent_changes, and explain_metric_change are unambiguous. Read paths (read_health_data vs get_metric_history vs read_measurements) and write paths (log_measurement vs log_daily_metric vs log_workout_session) are similarly well-separated.
All 20 tools use lower_snake_case with a clear verb_noun shape (get_*, read_*, log_*, calculate_*, detect_*, compare_*, find_*, export_*, clear_*), plus one consistent naming of the aggregate. There are no camelCase/naming aberrations or vague single-word verbs, so the pattern is highly predictable.
At 20 tools this is on the heavier end, but the domain genuinely spans raw observations, daily aggregates, workouts, imports, exports, and a layered analytics stack, and each tool earns its place. It sits just below the 'heavy' 16-25 range boundary, so it is slightly over but reasonable.
The surface covers ingestion (raw and daily), clearing/undo, multi-source provenance, freshness and import monitoring, CSV export, and a full analytics suite, which is near-complete for a quantified-self domain. Minor gaps exist, such as no explicit update/edit for an already-logged workout session (only day-level clear_metric), which an agent can work around.