google-health-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| GOOGLE_HEALTH_MCP_DB_PATH | No | Path to the SQLite cache database. | ~/.local/share/google-health-mcp/google_health.db |
| GOOGLE_HEALTH_MCP_OFFLINE | No | If truthy (1, true, yes, on), run as a cache-only reader without network access or credentials. | |
| GOOGLE_HEALTH_MCP_CONFIG_DIR | No | Directory holding the OAuth client and tokens. | ~/.config/google-health-mcp/ |
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": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| health_syncA | Sync health data to the local cache. Fetches data from the Google Health API and stores it in SQLite for fast offline queries. Run this before using other health_get_* tools. Syncs incrementally: only fetches data newer than the most recent entry in each table. First sync fetches the specified number of days. Args: data_types: What to sync. Options: "all", "heart_rate", "activity", "exercises", "sleep", "weight", "spo2", "hrv", "azm", "breathing_rate", "skin_temperature", "core_temperature", "cardio_fitness", "food_log", "ecg", "irn", "account", "height", "exercise_routes", "sleep_sessions", "weight_readings", "body_fat_readings". Comma-separated for multiple, e.g. "sleep,hrv". Default: "all". days: Days of history for first sync (default: 30). Ignored on subsequent syncs (uses last synced date). since: Optional "YYYY-MM-DD" backfill date. When set, fetches from this date regardless of what is already cached - use to pull history older than the current cache. Overrides incremental resume and days. until: Optional "YYYY-MM-DD" inclusive end date; requires since. Together they re-fetch and upsert exactly the since..until window - use to repair a gap in the middle of the cache without re-pulling everything from the gap to today. Returns summary of records synced per data type. Not for querying data - use health_get_heart_rate, health_get_activity, health_get_sleep, etc. instead. |
| health_get_activityA | Get daily activity summaries (steps, calories, active minutes, distance). Returns data from the local cache by default. Use live=True to fetch from the API. Run health_sync first to populate the cache. Note: live=True fetches one API call per day - avoid large ranges to stay within the 150 requests/hour rate limit. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per day with steps, calories, active minutes, distance. active_minutes = very_active + fairly_active (excludes lightly active). |
| health_trendsA | Analyse trends in cached health data. Computes averages and totals over time from the local cache, auto-syncing if stale. Args: data_type: What to analyse. Options: "heart_rate", "activity", "exercises", "sleep", "weight", "spo2", "hrv", "azm", "breathing_rate", "skin_temperature", "core_temperature", "cardio_fitness", "food_log". Default: "activity". period: Aggregation period. Options: "weekly", "monthly", "quarterly". Default: "monthly". start_date: Start date as "YYYY-MM-DD" or "365d". Default: last 12 months. end_date: End date as "YYYY-MM-DD". Default: today. compare: Compare two periods. Format: "last_30d vs previous_30d", "2026-03 vs 2026-02", "2026-Q1 vs 2025-Q4". When set, period/start_date/end_date are ignored. Returns aggregated averages per period. For activity: steps, distance, active minutes. For exercises: sessions, duration, calories. For sleep: duration, efficiency, stage breakdown. For heart_rate: resting HR min/avg/max. For weight: weight, fat%, BMI. For hrv: daily and deep RMSSD. For cardio_fitness, avg_vo2_max_low/high average a reported band while avg_vo2_max averages a single value: two definitions, each with the count of readings behind it, never one series. For spo2, min_spo2/max_spo2 are the lowest and highest bound among imported nights (counted by nights_observed_extremes), and avg_nightly_ci_low/avg_nightly_ci_high the mean of the nightly bounds this API reports (counted by nights_confidence_interval). These fields and their counts appear in the period form only.
For core_temperature, a reading whose value was changed in the app is counted as well as its earlier value, in both forms: check a high max_temp_celsius or readings_ge_38c with health_get_core_temperature. Not for raw data - use health_get_* tools instead. |
| health_get_azmA | Get daily Active Zone Minutes (AZM), the headline cardio metric. AZM counts minutes spent in heart rate zones at or above Fat Burn intensity.
Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per day with total_minutes plus per-zone breakdown (fat_burn_minutes, cardio_minutes, peak_minutes). Distinct from active_minutes in health_get_activity, which counts wall-clock minutes regardless of intensity - and which has no source in this API, so it is present only for days that arrived by import. |
| health_get_breathing_rateA | Get nightly breathing rate (avg breaths per minute during sleep). Sourced during sleep tracking. Useful as an illness/recovery signal: sustained increases of 2-3 bpm above personal baseline can indicate incipient infection or strain. Returns from cache by default, auto-syncing if stale. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per night with breaths_per_min and data_source (the device and platform Google says recorded it). Typical adult range: 12-20 bpm at rest. |
| health_get_cardio_fitnessA | Get Cardio Fitness Score (VO2 Max estimate). Estimated from resting HR, HR during walks and runs, and demographics. Updates roughly weekly. Returns from cache by default, auto-syncing if stale. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. A reading usually carries a range or a single value; one re-synced across the switchover between providers can hold both. They are different measurements - do not average them together or fill one from the other. vo2_max_low and vo2_max_high are a reported band (e.g. 39-43); vo2_max is a single figure. All in mL/kg/min, higher being better cardiorespiratory fitness. Beside a single figure Google may report cardio_fitness_level (POOR to EXCELLENT), estimated (1 or 0), vo2_max_covariance and data_source (the device and platform it says recorded it). |
| health_get_devicesA | List paired devices with battery level and last sync time. Live-only (no caching) - reflects current device state. Useful for monitoring tracker health, knowing which device produced data, and spotting sync gaps. Returns one entry per paired device with id, type, device_version, battery (e.g. "High"), battery_level (0-100), last_sync_time, mac, and features list. |
| health_get_exercisesA | Get exercise log entries (individual tracked activities). Returns exercise sessions from the local cache by default. Use live=True to fetch from the API. Run health_sync first to populate the cache. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. exercise_type: Filter by activity name (case-insensitive substring match), e.g. "cycling", "walk", "run". Default: all types. A value matching no workout name the cache holds is refused, naming those, rather than answered as a period with no workouts. live: If true, re-fetch this window from the API before reading the cache. include_detail: If true, include every exercise event (start, pause, stop), split and split summary. Without it each entry carries only how many there are (detail_counts), null where none is stored: an imported workout, one Google sent none for, one synced before these were fetched, or a stored value that cannot be read. Returns exercise entries with name, duration, calories, avg heart rate, distance, and source (for a synced workout the recording device's name, and data_source.recordingMethod says whether it was logged by hand; for an imported one whatever the import carried), and for a synced workout also start and end times with their UTC offsets, active_seconds, notes, Google's metrics_summary whole (pace, speed, elevation, heart-rate zone durations, mobility), exercise_metadata (hasGps, pool length) and data_source. Note: HR data from cycling may be unreliable (optical sensor vs handlebar grip). |
| health_get_exercise_routeA | Get the GPS route of one exercise, as the TCX file Google exports. Only exercises recorded with GPS have a route, and a sync fetches routes for the workouts in its own window. The TCX text carries every trackpoint (time, position, altitude, distance, heart rate) and can run to hundreds of kilobytes, so it is returned only when asked for. Args:
log_id: The exercise's Returns the exercise's log_id, date and provider, and the TCX text when asked for. |
| health_get_food_logA | Get daily food and water log summary. Returns calories consumed and water intake (in mL) per day. Only populated where the user logs food or water by hand in a connected app. Returns from cache by default, auto-syncing if stale. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per day with calories_in and water_ml. Days with no logging are omitted. |
| health_get_heart_rateA | Get daily resting heart rate and heart rate zones. Returns resting HR and zone breakdown (Out of Range, Fat Burn, Cardio, Peak) from the local cache by default, auto-syncing if stale. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per day with resting_hr, zones array, calculation_method (Google's WITH_SLEEP or ONLY_WITH_AWAKE_DATA) and data_source (the device and platform Google says recorded it). Zone data: name, minutes, caloriesOut, max/min HR for each zone. Zones are present only on days that arrived by import; a sync does not write them. |
| health_get_hrvA | Get nightly HRV (heart rate variability) data. Returns data from the local cache by default. Use live=True to fetch from the API. Run health_sync first to populate the cache. HRV data is sparse: only nights with on-wrist sleep tracking produce readings. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per night with daily_rmssd and deep_rmssd (ms), entropy, non_rem_hr (non-REM heart rate, bpm) and data_source (the device and platform Google says recorded it). RMSSD = root mean square of successive RR interval differences. Higher values generally indicate better recovery and parasympathetic activity. |
| health_get_lifetime_statsA | Get all-time activity totals and personal best records. Totals for steps, floors, distance and calories over every day held in the cache, plus the best single day for steps, floors and distance with the date each record was set. The answer is bounded by what has been synced, so it is returned with a
|
| health_get_profileA | Get the account's profile, settings and irregular-rhythm enrolment. Each is the record Google returns, kept whole: the profile carries age, membership start and stride lengths, the settings carry units, time zone and locale, and the enrolment says whether irregular-rhythm notifications are on. Returns data from the local cache by default. Args: live: If true, re-fetch the records from the API before reading the cache. Returns |
| health_get_ecgA | Get electrocardiogram (ECG) readings taken on the watch. Each reading is a single-lead trace the user started by hand, with a rhythm classification - NORMAL_SINUS_RHYTHM, ATRIAL_FIBRILLATION, or one of several inconclusive results (low or high heart rate, poor reading, unclassified). Use it for questions about heart rhythm or AFib checks; for resting rate over time use health_get_heart_rate instead. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. include_waveform: If true, include the raw voltage samples. These are thousands of integers per reading - ask for them only to analyse the trace itself. Multiply by scaling_factor for millivolts, and read them at sampling_hz samples per second. Returns one entry per reading with classification, avg_bpm, duration_sec, sampling_hz, scaling_factor and waveform_samples (null where no trace was stored, or where the stored one cannot be read). |
| health_get_irregular_rhythmA | Get irregular heart rhythm notifications raised by the watch. These are background checks the watch runs while the user is still - an alert means it saw a rhythm consistent with atrial fibrillation over one or more windows, not a diagnosis. Most accounts never have one. For a deliberate reading with a trace behind it, use health_get_ecg. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per notification with start_time, end_time and alert_windows (the periods that triggered it). |
| health_get_sleepA | Get nightly sleep data (duration, stages, efficiency). Returns sleep data from the local cache by default. Use live=True to fetch from the API. Run health_sync first to populate the cache. Sleep data is sparse: only nights with watch-tracked sleep are present. Travel, off-wrist nights, or manual logs may be missing. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per night with total_minutes, efficiency, start/end times, and stage breakdown (deep, light, REM, wake minutes). |
| health_get_sleep_sessionsA | Get each sleep session as Google recorded it, rather than the nightly total. A night can be several sessions (a wake-and-resume, a nap); health_get_sleep sums them per night, while this returns each one whole: its interval, type (CLASSIC or STAGES), Google's metadata (mainSleep, nap, processed, manuallyEdited, stagesStatus), its summary (minutesAsleep, minutesAwake, minutesInSleepPeriod, minutesToFallAsleep, minutesAfterWakeUp, and the per-stage minutes and counts), and the device that recorded it. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. include_stages: If true, include every stage segment, short awakening and out-of-bed segment. Without it each session carries only how many there are, since a night runs to dozens. Returns one entry per session with session_id, date (the local day it ended), start_time, end_time, provider, record (the session as Google sent it, less the segment arrays unless include_stages) and segment_counts (how many of each; absent where the stored record cannot be read). |
| health_get_spo2A | Get nightly SpO2 (blood oxygen saturation) data. Returns data from the local cache by default. Run health_sync first to populate it. SpO2 data is sparse: only nights with on-wrist sleep tracking produce readings. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per night: avg SpO2 percentage and up to two pairs of bounds. avg_ci_low/avg_ci_high come from this API, which Google describes as the lower and upper bound of the confidence interval of oxygen saturation samples during sleep. min/max hold whatever an import carried; on the history checked, they matched avg_ci_low/avg_ci_high on every night carrying both. std_dev is Google's standard deviation of the daily SpO2 averages over the past 7-30 days, not of this night's samples, and data_source the device and platform Google says recorded it. Normal range: 95-100%. Below 90% may indicate sleep apnea. |
| health_get_skin_temperatureA | Get nightly skin temperature variation (degrees Celsius from personal baseline). This is the device-derived RELATIVE deviation recorded during sleep, NOT an absolute body temperature - for fever / body-temperature readings use health_get_core_temperature instead. A baseline takes about three nights to establish before values appear. Useful as an illness/cycle/recovery signal. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per night with nightly_relative (degrees C, can be negative), the absolutes behind it (nightly_absolute, baseline), nightly_stddev_30d (the 30-day standard deviation of the relative value), log_type (e.g. "dermal") and data_source (the device and platform Google says recorded it). |
| health_get_core_temperatureA | Get manually-logged core (body) temperature readings (degrees Celsius). These are absolute body temperatures the user enters by hand - a thermometer reading logged in an app - and are the right source for fever / body-temperature questions. They are NOT the device-derived nightly skin-temperature variation from health_get_skin_temperature. A single day can hold several readings (each timestamped), useful for tracking a fever over time. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per logged reading with datetime (YYYY-MM-DDThh:mm:ss), temp_celsius, measurement_location (where it was taken, e.g. "MOUTH" or "EAR", as logged), reading_id and data_source. A reading whose value is changed in the app is kept beside its earlier value rather than replacing it, so two readings logged at the same datetime may be one reading, edited. |
| health_get_weightA | Get weight log entries (weight, BMI, body fat percentage). Returns data from the local cache by default. Use live=True to fetch from the API. Run health_sync first to populate the cache. Weight data is sparse: only days with weigh-in entries are present. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns one entry per day with weight_kg, bmi, fat_pct. Every individual weigh-in is in health_get_weight_readings. |
| health_get_weight_readingsA | Get every weigh-in and body-fat reading, as Google recorded each one. health_get_weight keeps one row a day; this returns every reading: its value in Google's own unit (weightGrams, percentage), when it was taken, and the device or app that recorded it. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "30d". Default: last 30 days. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch this window from the API before reading the cache. Returns weight_readings and body_fat_readings, each entry with reading_id, datetime (local), date, provider and record (the reading as Google sent it), and count, the number of readings of both kinds. |
| health_get_heightA | Get height readings, in millimetres as Google reports them. Height is usually logged once, often years back, so the default window is the last ten years. Returns data from the local cache by default. Args: start_date: Start date as "YYYY-MM-DD", "YYYY-MM", or "Nd". Default: ten years back. end_date: End date as "YYYY-MM-DD". Default: today. live: If true, re-fetch height from the API before reading the cache. Returns one entry per reading with datetime, date, height_mm and provider. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 24 tools
Most tools target clearly distinct metrics or actions, and descriptions explicitly separate overlapping granularities like sleep vs sleep_sessions, weight vs weight_readings, and skin vs core temperature. A few zone-related (heart_rate vs AZM) or aggregate-vs-raw (trends vs getters) pairs could cause brief hesitation, but the descriptions resolve them.
All names use snake_case with a consistent health_ prefix, and getters follow the health_get_<metric> pattern. Minor deviations are health_sync and health_trends, which are readable and conventional for their distinct actions.
At 24 tools, the surface is in the heavy 16-25 range and creates a large selection space for an agent. The breadth of distinct Google Health metrics means few tools are truly redundant, but some consolidation (e.g., weight vs weight_readings, sleep vs sleep_sessions) could reduce the load.
The set covers a wide range of health data types—activity, heart, sleep, body composition, temperature, food, ECG, devices, profile—plus sync, trends, and all-time stats. For a read-only health API, there are no obvious dead ends; every major metric appears accessible.