Skip to main content
Glama
PhilipAD

MetricBridge (formerly Health Export AI)

health-export-mcp

An MCP server for your own Apple Health data. 194 metrics, 18 tools (16 read-only, plus two that log events to their own file) and 22 prompts over local stdio, with zero dependencies, no account and no server of ours in the path. Your agent gets JSON with units, date ranges and coverage, and answers from your numbers.

Docs and per-client config: healthexport.dev/mcp · agents can run the setup from SKILL.md · privacy: the server reads a local file and makes no network requests, so your health data never reaches us or a cloud AI through it.

Install: one command

claude mcp add health-export -- npx -y health-export-mcp

On a Mac that is the whole install: since 1.7.3 the server finds the MetricBridge app's iCloud Drive folder by itself. On Windows or Linux, or for an export in another folder, add --env "HEALTH_DATA_DIR=/path/to/folder" before the --. Needs Node.js 18 or later. Claude Desktop: open health-export.mcpb instead (no JSON, no Node on your PATH). Cursor, VS Code, opencode, OpenClaw, Hermes: AGENTS.md.

Then ask "Use health-export: how did my HRV this week compare with last week?" The agent calls get_trends and gets numbers that carry their own provenance: unit, exact date ranges, whether each window was complete, and how much history the export holds. This is the real output on 1.7.3 (from the built-in --demo dataset, which is synthetic so it can be published; a real export answers with the same fields and without the watermark):

[SYNTHETIC DEMO DATA] {"metric":"heart_rate_variability","unit":"ms","window":7,"recent":50.859,"prior":51.306,
 "change":-0.447,"changePercent":-0.871,"direction":"flat","recentRange":{"from":"2026-08-12","to":"2026-08-18"},
 "recentDays":7,"priorDays":7,"windowSatisfied":true,"coverage":{"firstDate":"2025-07-15","lastDate":"2026-08-18","days":400}}

No iPhone yet? claude mcp add health-export-demo -- npx -y health-export-mcp --demo serves that watermarked synthetic dataset, so you can try every tool in 30 seconds before installing anything on a phone.

Related MCP server: Apple Health MCP Server

Use your real data (about 5 minutes the first time, needs an iPhone)

  1. Export. Install MetricBridge on the iPhone, allow Health read access and tap Export now. It writes .health-cache.json to iCloud Drive (or a folder, or your LAN). The app has a 7-day free trial, then one purchase ($24.99), no subscription. This server is MIT and free.

  2. Connect. The one command above (Claude Code), the .mcpb (Claude Desktop), or the block for your client in AGENTS.md.

  3. Check it arrived. npx -y health-export-mcp --doctor prints the folder it chose and why, each file's age, and freshness. iCloud can take a few minutes to bring a new export to the Mac.


What is health-export-mcp?

It's an MCP server that turns your Apple Health export into tools your AI agent can query in natural language: HRV, sleep, resting heart rate, steps, workouts, VO₂ max and 180+ more.

  • Who it's for: anyone who wants their AI to reason over their real health data instead of a stale CSV.

  • What it isn't: a cloud service. There's no developer server in the path, your data goes only where you point it.

  • Setup: point the server at your exported data and add it to your AI client.

  • Where the data comes from: the MetricBridge iOS app (formerly Health Export AI), available on the App Store, writes the export this server reads. The server still reports itself as health-export-ai in serverInfo, and the npm package and repository names are unchanged.

Just exported and the Mac has not seen it yet? iCloud can take a few minutes to sync the file across, so a check 60 seconds after tapping Run in the app can still show the old timestamp. get_mcp_status reports lastDataDate and the intraday lastWrite, which is the quickest way to tell "still syncing" from "never arrived".

Try it with no iPhone needed: npx -y health-export-mcp --demo (or node server.mjs --demo in a clone) serves a deterministic synthetic dataset (400 days, workouts, events, sleep sessions) with every answer watermarked as synthetic. Or run npm test to write a sample cache and exercise every tool.


Connect Apple Health to your AI agent: quickstart

1. Get your Apple Health data flowing

The companion iOS app MetricBridge exports your Apple Health data, read-only, automatic, private. For this MCP server, export to a destination it can read:

Destination

Notes

iCloud Drive (default)

Your Mac reads the synced folder automatically

Local folder

Any folder that syncs to your Mac (Dropbox, Google Drive, OneDrive, …)

LAN (HTTP / WebSocket)

Direct push to the server, great over Tailscale

Using a non-MCP tool (ChatGPT, n8n, Home Assistant)? The app can also POST to a webhook those tools read directly, see Works with.

2. Add the server to your agent

Fastest, one command (Claude Code):

claude mcp add health-export -- npx -y health-export-mcp
# Windows, Linux, or an export outside iCloud Drive:
claude mcp add health-export --env "HEALTH_DATA_DIR=/path/to/folder" -- npx -y health-export-mcp

Everything before -- is an option for claude mcp add, everything after it is the command that starts the server. On a Mac the server reads the app's iCloud Drive folder when HEALTH_DATA_DIR is unset (1.7.3+); if you set it to that folder by hand, double-quote it (the folder name contains a space) and use $HOME, because a quoted ~ is not expanded by the shell. Add --scope user to make the server available in every project (the default scope is the current project only). Check it with claude mcp list.

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json), and any client that takes an mcpServers block:

{
  "mcpServers": {
    "health-export": {
      "command": "npx",
      "args": ["-y", "health-export-mcp"]
    }
  }
}

Off a Mac, or for another folder, add "env": { "HEALTH_DATA_DIR": "/path/to/folder" } to the entry (a leading ~ is expanded by the server). npx -y fetches the published package at run time without an install prompt (npm verifies package integrity), so it runs anywhere with no clone or absolute path. The server expands a leading ~ in HEALTH_DATA_DIR itself, so the JSON form can keep it. Prefer to pin a vetted local checkout instead? Use "command": "node", "args": ["REPLACE_WITH_ABSOLUTE_PATH/server.mjs"], get the path with node -e "console.log(process.cwd()+'/server.mjs')" inside the repo. Or skip JSON entirely, drag health-export.mcpb into Claude Desktop → Settings → Extensions.

Auto-configure from a clone:

git clone https://github.com/PhilipAD/health-export-mcp.git
cd health-export-mcp
node apply-mcp-config.mjs     # detects installed clients and writes the config for you

Cursor / VS Code: node gen-deeplinks.mjs prints one-click install links. opencode / OpenClaw / Hermes: see AGENTS.md for the exact block, same shape, one per client.

3. Ask your agent

Restart the client and try:

"Use health-export: what's my average HRV this week vs last week?"


Works with Claude, Cursor, ChatGPT, OpenClaw, Hermes

Client / Agent

Integration

How

Claude Code

Native MCP

claude mcp add ... -- npx -y health-export-mcp

Claude Desktop

Native MCP

.mcpb bundle, or mcpServers block

Cursor

Native MCP

One-click deeplink, or ~/.cursor/mcp.json

opencode

Native MCP

opencode.json mcp block

OpenClaw

Native MCP

Add the server block to your MCP config

Hermes

Native MCP

Add the server block to your agent's MCP config

VS Code (Copilot / Continue)

Native MCP

One-click deeplink

ChatGPT · Gemini · Grok

Webhook*

Consume the app's webhook export

n8n · Home Assistant

Webhook*

Trigger automations on the exported JSON

*MCP clients query this server directly over stdio. ChatGPT / Gemini / Grok / n8n / Home Assistant don't speak MCP, they consume the same Apple Health data via the iOS app's token-authenticated webhook export.


The 18 MCP tools

Full reference with request/response examples: healthexport.dev/mcp.

Tool

What it does

get_mcp_status

Health check: source, metric/workout counts, which context files exist, latest data date, and a freshness summary. Call first.

get_freshness

How current the export is: stale judged on the core files (daily cache, sleep sessions) against a stated threshold (26 h by default, HEALTH_STALE_AFTER_HOURS, or maxAgeHours), age_hours, as_of, last_data_date, per-file write times. state: "stale_copy" (with a stale_copy list) when the phone's .health-manifest.json or iCloud says this Mac holds an older copy of a core file than the phone wrote. Same rule as the status --max-age cron gate.

list_metrics

Every available metric with unit, day count, and date range.

get_health_metrics

Daily values for a metric (or all) over a date range + an aggregate (avg/sum/min/max/latest). Supports filterDays (restrict to days covered by logged events, with negate). Blood pressure at day granularity also lists the individual readings (health-readings.json).

get_trends

Recent N-day window vs the prior N days: change, % change, direction. Supports excludeTravelDays.

compare_periods

A metric across two arbitrary date periods (A vs B), or around a logged event via anchor: {eventId, days} (the event day excluded from both sides).

get_structured_export

Clean JSON for chosen metrics/range, paginated with a cursor.

get_intraday

The hour-by-hour window from the app's hourly automations (health-intraday.json, app 1.4+): yesterday and today, replaced each run, with each hour's Min/Max for discrete metrics when the app writes them.

query_health_data

Natural-language convenience: "average HRV last month" routed to structured results.

list_events

Logged context events (medication, habit, visit, life, shift, episode, travel) with type/tag/date filters, from the app and from add_event, each labelled source: "app" or "agent".

add_event

Log an event the user states (a medication start, a trip) into health-events-agent.json, the one file this server writes. Idempotent by clientEventId.

remove_event

Remove an event logged with add_event. Events logged in the iOS app are edited there.

get_profile

Opted-in context fields (conditions, medications, goals, allergies, notes) plus presentFields. Absent fields were withheld, never "none".

get_workouts

Workouts by activity/date with pagination; includes heart rate, running dynamics, cycling power, intervals and hasRoute when exported.

get_sleep_sessions

Clustered sleep sessions attributed to the waking day; split nights returned as separate sessions, duplicate copies of one night collapsed (collapsedDuplicates).

get_cycle_context

Day-in-cycle and coarse phase derived from logged period starts. Never predictive.

correlate_metrics

Pearson correlation between two metrics with a 0..3 day lag. Association, not causation, always stated.

resolve_metric

Canonical name and unit for any spelling (steps, StepCount, Body Weight, hrv), unit variants with exact formulas, an optional {value, unit} conversion, and whether the export holds it. Ambiguous words come back with candidates, never guessed. The data tools accept the same aliases and report resolvedFrom. When the export carries per-source attribution (app builds that write src/by on daily points and a health-sources.json, cache schema minor 2), the answer adds a sources block naming the devices that fed the export, which one won each metric and the priority rules, and sourceMerge reports disagreement as visible; older exports report it as not available.

Coverage: 194 Apple Health metrics across activity, heart, HRV, mobility, respiratory, body, sleep, hearing, and nutrition, plus workouts. Data answers carry honest coverage blocks, and single-metric answers list logged events inside the window as segmentBoundaries so an average across a medication start or life change cannot masquerade as one regime.

Data files

The daily cache (.health-cache.json) and workouts cache are joined by optional context files, all optional: health-events.json (the logging surface), health-profile.json (opt-in context), health-sessions.json (sleep sessions), health-cycles.json (observed cycle starts), health-days.json (timezone change log), health-readings.json (individual blood pressure readings) and .health-manifest.json (what the phone last wrote, for the stale-copy check). The server reads all of them and writes only health-events-agent.json (events logged with add_event). An absent file is reported as available:false with a note, never as "no data". Formats: docs/SCHEMA-CONTRACTS.md.

Two patterns worth knowing: query a parent's Apple Health from your own AI (consent-first, the parent's phone is the authority) and logging data into Apple Health via Shortcuts (the server never writes to Apple Health).

MCP prompts

The server ships 22 prompts over prompts/list / prompts/get: daily brief, weekly review, doctor visit prep, what changed since my last visit, sleep quality and regularity, HRV trend, training and race week reviews, zone minutes, n-of-1 experiment, medication before/after, GLP-1 dose-step compare, sobriety milestone, shift block compare, travel-honest monthly review, cycle-aware trend read, caregiver check-in, glucose day summary, long-term activity narrative, data coverage audit, and a profile-aware context bootstrap. Every prompt leads with coverage honesty and never turns data into diagnosis.

Demo mode

node server.mjs --demo (or HEALTH_DEMO=1) serves a deterministic synthetic dataset: ~15 metrics over 400 days, 30 workouts with intervals, 8 events, a profile, 60 sleep sessions, cycle starts and a timezone change, anchored to a fixed date with a seeded PRNG. Every answer carries demo: true and a [SYNTHETIC DEMO DATA] text prefix, so demo output can never pass for a real export.

CLI

node server.mjs --doctor            # diagnostics: files, sizes, schema, freshness, pairing
node server.mjs status --max-age 24 # exit 0 if data is fresh, 1 if stale or a stale iCloud copy (cron gate)
node server.mjs receive             # standalone LAN receiver (binds 127.0.0.1 by default)
node server.mjs --help

Example AI queries

"What has my resting heart rate done over the last 30 days?"
"Compare my deep sleep this week vs last week."
"Is my VO₂ max trending up or down this quarter?"
"Give me a clean JSON export of HRV, RHR and sleep for the last 14 days."
"Correlate my step count with my sleep duration this month."

Use cases

  • AI health coach: let an agent reason over your real HRV, resting heart rate, and sleep to suggest when to push and when to recover, grounded in your actual numbers instead of generic advice.

  • Training-load analysis: pull workouts, VO₂ max, and heart-rate trends so your agent can flag overreaching, plot fitness progression, and pace a training block.

  • Sleep correlations: have your agent correlate deep-sleep duration against steps, caffeine, late workouts, or screen time to see what tends to go with your better nights (associations, not causes).

  • Quantified-self dashboards: feed clean JSON for any metric set and date range straight into a notebook, spreadsheet, or LLM-built dashboard for your own self-tracking.

  • Personal research & experiments: run n-of-1 experiments (supplement, routine, or protocol changes) and let an agent compare before/after periods across 194 metrics to see what changed.


Building a wellness or coaching product?

This server reads a file the MetricBridge iOS app writes on the user's own device, with no central health-data processor in the path. The same app can POST signed JSON (Standard Webhooks v1, published JSON Schemas) straight to your endpoint, so your users bring their own Apple Health to your product without an SDK in your app and without any vendor server holding the data. The offer, flat pricing and boundaries: healthexport.dev/platforms. The integration guide and the zero-dependency conformance kit: healthexport.dev/docs/platforms. Wellness-scoped and end-user-directed; no clinical or HIPAA work.


The app that feeds it


Privacy & security

The one non-health thing the server reads from the export is _meta.license, a marker the app writes with exactly two shapes: {"state": "trial", "daysRemaining": N} or {"state": "unlocked"}. A fresh export from a trial adds one sentence to get_freshness naming when the trial ends; an unlocked export adds nothing. No network call is involved and nothing is sent anywhere.

  • Reads your export, never Apple Health. The server never touches HealthKit. Its only write is health-events-agent.json, the events an agent logs with add_event / remove_event; it never modifies a file the app writes. Both tools are refused in demo mode and on a read-only folder, and the file is sealed like the rest of an encrypted export.

  • Local-first. It runs on your machine over stdio. There is no developer server in the path.

  • Optional pairing. Set PAIRING_SECRET to the code the iOS app shows (Settings → Agent pairing) to gate access.

  • Encrypted exports (optional). If you switch on Export encryption in the iOS app, set HEALTH_EXPORT_PASSPHRASE (or HEALTH_EXPORT_PASSPHRASE_FILE) to the same passphrase and the server decrypts on your machine. See Encrypted exports.

  • No telemetry. The server makes no network requests. get_mcp_status always includes one static link to the companion app's website (and its App Store page), so the bridge is attributable back to install. Both links carry fixed campaign labels (utm_source=mcp, utm_medium=agent-tool, utm_campaign=mcp-unlock; ct=mcpserver, ppid=…) that are identical for every user and contain nothing about you or your data; they only count if you choose to open one.

  • Auditable. Zero runtime dependencies, about 4,000 lines of plain ES modules and no build step: what's on npm is what's in this repo, and npm provenance links each tarball to the commit and workflow that built it.

  • Signed releases. Hosted artifacts are minisign-signed and checksummed, see Verifying releases.


Encrypted exports

The iOS app can encrypt what it exports with a passphrase you choose (Settings > Export encryption, switched on per destination: iCloud Drive, a folder, your server, the local network). Give the server the same passphrase and every file is decrypted transparently, in memory, on this machine:

HEALTH_EXPORT_PASSPHRASE='your passphrase' HEALTH_DATA_DIR=... node server.mjs
# or keep it out of your shell history and process list:
HEALTH_EXPORT_PASSPHRASE_FILE=~/.config/metricbridge/passphrase HEALTH_DATA_DIR=... node server.mjs

If both are set, HEALTH_EXPORT_PASSPHRASE wins; one trailing newline in the file is ignored and an empty value counts as unset. The .mcpb bundle has an optional Export passphrase field. Plaintext exports keep working either way. A missing or wrong passphrase, or a file that fails its integrity check, is reported as such (get_mcp_status says encrypted: true plus the reason, --doctor prints an encryption: line), never as "no data". The passphrase is never logged or echoed.

The LAN receiver (HEALTH_LISTEN=1) decrypts encrypted pushes with the same variable and answers 422 with a machine reason (passphrase_missing, passphrase_mismatch, tampered, malformed, unsupported_version, passphrase_file_unreadable, encryption_required). The pairing token rides in a plain http header on that leg, so set HEALTH_REQUIRE_ENCRYPTED=1 to refuse plaintext pushes once the app encrypts that leg. To decrypt one body yourself (a webhook you run, a script):

HEALTH_EXPORT_PASSPHRASE='...' node envelope.mjs open body.json     # prints the decrypted JSON

Format (metricbridge.enc v1, envelope.mjs, node:crypto only): a JSON object {format, v, alg: "A256GCM", kdf: "PBKDF2-HMAC-SHA256", iter, salt, kcv, nonce, ct}. master = PBKDF2-HMAC-SHA256(NFC(passphrase), salt[16], iter) (600,000 iterations, the OWASP figure); an AES-256-GCM key and an 8-byte key-check value come off it with HKDF-SHA256; each message gets a random 12-byte nonce; every header field is bound in as AAD as the string metricbridge.enc|v=1|alg=A256GCM|kdf=PBKDF2-HMAC-SHA256|iter=<n>|salt=<b64>|kcv=<b64>|nonce=<b64>. Base64 is canonical with padding. test/fixtures/ holds envelopes made by the iOS app's Swift encoder and by this module; envelope.test.mjs proves both open and regenerate byte for byte.

What it protects: the export at rest in the destination (iCloud Drive, a synced folder, a webhook host's storage) and the LAN push on the wire, plus integrity of every sealed file. What it does not: an unlocked phone, this computer (it holds the passphrase), metadata (sizes, timing), rollback or replay of a whole sealed file, files you share by hand from the app, or a weak passphrase (anyone with the files can guess offline; the app requires at least 8 characters).


Verifying releases

The server artifacts hosted at healthexport.dev/mcp/ (used by the setup skill) are minisign-signed, and every download is SHA-256 checksummed. The signing public key is published in this repo (minisign.pub) and in SKILL.md, pin it from here, not only from the website, so a compromise of the website alone cannot swap both an artifact and its key.

PUBKEY='RWS6TxVWSKUblYkx7Db6ZpmvHALwHpznZpjaED/FlZj+PpxSlel0MxHZ'   # = minisign.pub in this repo
curl -fsSL https://healthexport.dev/mcp/SHA256SUMS         -o SHA256SUMS
curl -fsSL https://healthexport.dev/mcp/SHA256SUMS.minisig -o SHA256SUMS.minisig
minisign -Vm SHA256SUMS -P "$PUBKEY" || { echo "signature INVALID, do not run"; exit 1; }   # fail closed
curl -fsSL https://healthexport.dev/mcp/server.mjs -o server.mjs
shasum -a 256 --ignore-missing -c SHA256SUMS || { echo "checksum mismatch, do not run"; exit 1; }

The pinned, checksum-verified download above is the locked-down path. npx -y health-export-mcp instead resolves the latest version published to npm at run time (npm provides its own package integrity). GitHub releases carry a SHA256SUMS file and a CycloneDX SBOM (*.cdx.json); a SHA256SUMS.minisig made with the same key is attached once the maintainer has signed that release locally, so check for it before relying on it. Full policy, including npm provenance and how to report a vulnerability privately: SECURITY.md. Security overview: https://healthexport.dev/security.


Requirements

  • Node.js ≥ 18 (node -v).

  • A folder containing a .health-cache.json exported by MetricBridge, or run npm test to generate a sample one.

  • An MCP-compatible client (Claude Desktop, Cursor, opencode, OpenClaw, Hermes, VS Code), or any tool that can read the webhook export.


FAQ

How do I get my Apple Health data into Claude / ChatGPT / my AI agent? Install health-export-mcp, export your Apple Health data with the MetricBridge iOS app (to iCloud, a folder, or your LAN), then add the MCP server to your AI client. Your agent can then query your Apple Health metrics in natural language. (Non-MCP tools like ChatGPT read the app's webhook export instead.)

Is my health data sent to a server? Not to us. The MCP server runs locally and reads only the export files or endpoints you configure, there's no developer server in the path. Where your iOS export is delivered (iCloud, your LAN, a webhook) is entirely your choice.

Which agents are supported? Any MCP client, Claude Desktop, Cursor, opencode, OpenClaw, Hermes, VS Code, natively. ChatGPT, Gemini, Grok, n8n, and Home Assistant consume the same data via webhook.

Do I need the iOS app? For your own data, yes: the server reads the file the MetricBridge app writes and never talks to HealthKit itself. To evaluate first, --demo serves a watermarked synthetic dataset with no app and no iPhone.


Troubleshooting

  • "No metrics found": run npx -y health-export-mcp --doctor. It prints the folder the server chose and why (HEALTH_DATA_DIR, the iCloud default on a Mac, or the current directory) and whether .health-cache.json is there. Confirm the app has exported at least once; get_mcp_status shows the same source and the latest date.

  • Server not visible in the client: run npx -y health-export-mcp --doctor in a terminal. If it works there, the client can't find npx (common for Claude Desktop with Node from nvm or Homebrew): use the .mcpb, or set "command" to the full path from which npx. Then fully quit and reopen the client.

  • Exports stopped after a week: the app's free trial ended. get_freshness reports the export as stale and links the unlock page; once the app is unlocked, exports resume with no change on this side.

  • Answers miss last night, or state: "stale_copy": this Mac's iCloud Drive has an older copy of the export than the phone wrote. Wait a few minutes, or open the MetricBridge folder in iCloud Drive in Finder and choose Download Now on the file (Remove Download drops only this Mac's local copy, and it downloads again). --doctor lists each stale copy.

  • Locked data error: the export file is protected until first unlock after reboot; unlock your device once.


Run it locally

# Node ≥18, no install needed
HEALTH_DATA_DIR=~/Library/Mobile\ Documents/iCloud~ai~healthexport~app/Documents node server.mjs

# integration test, writes a 14-day sample cache and exercises every tool
npm test

stdio transport (newline-delimited JSON-RPC 2.0), the universal MCP transport. Optionally set HEALTH_LISTEN=1 to also accept LAN pushes from the iOS app in the same process (see receiver.mjs).


How it works

The iOS app reads Apple Health (read-only) and writes a compact .health-cache.json (plus optional context files) to the destination you choose. This server reads those files and exposes the 18 tools above over MCP. No bridge, no Docker, no database: just files and stdio.

Apple Health → MetricBridge (iOS) → .health-cache.json → health-export-mcp → MCP client → you

Other Apple Health MCP servers in the ecosystem, worth a look depending on your setup:

How health-export-mcp differs: the iPhone app keeps the export current in the background, so there's no manual export.xml and no re-import; the server has zero dependencies and needs no Docker, database or Mac app; every answer carries its units, date range and coverage; and it never writes to Apple Health: its only write is the events file your agent logs to.


If this helps your setup, a ⭐ makes it easier for others to find.

License

MIT, see LICENSE. Contributions welcome.


Available Tools

18 tools
add_eventA
Idempotent

Log a context event the user tells you about (a medication start, a habit change, a visit, a trip) so later analysis can segment around it. Writes ONLY health-events-agent.json in the export folder, never Apple Health and never a file the iOS app writes; the app shows these events read-only. Returns the stored event with its id (starting "agent-"). Idempotent: repeating a call with the same clientEventId (or, without one, the same date, type, title and endDate) returns the event already logged instead of adding a duplicate. Ask the user before logging anything they did not state. Refused in demo mode, when the folder is not writable, and on an encrypted export without HEALTH_EXPORT_PASSPHRASE (the file is sealed like the rest of the export when the passphrase is set).

NameRequiredDescriptionDefault
dateYesLocal day the event happened or started, YYYY-MM-DD.
noteNoOptional longer note, at most 2000 characters.
tagsNoOptional tags for filtering, e.g. ["magnesium"]. At most 20.
typeYesEvent type: medication, habit, visit, life, shift, episode, travel or other. Range types (shift, travel, episode) usually carry an endDate.
titleYesShort description, e.g. "Started magnesium 300 mg". At most 200 characters.
endDateNoLast day of a range event (inclusive), YYYY-MM-DD, not before date. Omit for a point event.
clientEventIdNoOptional idempotency key: the same key never logs a second event.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds substantially beyond annotations: exact file written, explicit statement that Apple Health and app-owned files are untouched, read-only visibility in the app, the returned id prefix, idempotency fallback rules, and three distinct refusal conditions including passphrase sealing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded, then constraints, then return value, then failure modes — a sensible order. Dense but every clause carries information; the file-path clause is slightly verbose but justified by the safety point it makes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description still specifies the return shape (stored event with agent-prefixed id). Mutation side effects, idempotency, and failure modes are all covered, so an agent can call it safely without further context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3; the description earns extra by explaining the idempotency fallback (same date/type/title/endDate when no clientEventId is supplied), which the schema does not state. Range-vs-point event semantics are left to the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource ("Log a context event") with concrete examples (medication start, habit change, visit, trip) that map onto the type enum. Clearly a write-side counterpart to list_events/remove_event in the sibling set.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear trigger ("the user tells you about") and an explicit precondition ("Ask the user before logging anything they did not state"). Refusal conditions (demo mode, unwritable folder, encrypted export without passphrase) are enumerated, though no sibling tool is named as an alternative for reading these events back.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

compare_periodsA
Read-onlyIdempotent

Compare a metric between two arbitrary date periods (A vs B): each aggregate plus the change and percent change. Pass periodA/periodB explicitly, or pass anchor {eventId, days} to build both periods around a logged event (the before/after question, with the event day excluded from both sides).

NameRequiredDescriptionDefault
anchorNoBuild both periods around a logged event instead of passing dates: periodA is the {days} days before the event date, periodB the {days} days after, the event day itself excluded from both. Get event ids from list_events (app and agent-logged events both work). Pass either anchor or periodA/periodB, not both.
metricYes
periodANo
periodBNo
excludeTravelDaysNoDrop days on which a timezone change landed (health-days.json): those days were not 24 hours long, so their totals are stretched or shortened by the clock. The answer reports how many days were excluded and why.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover safety (readOnlyHint, idempotentHint, openWorldHint=false), so the description's job is to add context — and it does: the event day is excluded from both sides, and the answer contains aggregate, change, and percent change. It stops short of noting how missing data or partial periods are handled.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences that front-load the core comparison and then the anchor shortcut; nearly every clause earns its place. The nested parentheticals make it slightly harder to scan than a truly crisp definition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description helpfully sketches the return values, and the anchor semantics are covered. The gap is the required metric argument: an agent still doesn't know what strings are legal or whether it must call resolve_metric first.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 40%, so the description must compensate. It explains the anchor-vs-periodA/periodB tradeoff and the event-day exclusion well, but the required 'metric' parameter is documented nowhere — no valid values or pointer to list_metrics/resolve_metric — and excludeTravelDays is left entirely to the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb and resource — 'Compare a metric between two arbitrary date periods (A vs B)' — and even states the shape of the answer (each aggregate plus the change and percent change). It does not name a sibling such as get_trends or correlate_metrics to sharpen the boundary, which keeps it below a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives two clear invocation modes: pass periodA/periodB explicitly, or pass anchor {eventId, days} for the 'before/after question'. That is real usage guidance, but it offers no exclusions or comparison against alternatives like get_trends or correlate_metrics.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

correlate_metricsA
Read-onlyIdempotent

Pearson correlation between two metrics' daily values: {metricA, metricB, lag (0 to 3, default 0), start, end}. lag pairs metricA on day d with metricB on day d+lag, so lag 1 compares against the FOLLOWING day. Returns alignedPairs, r (withheld below 10 aligned pairs), and both means. The answer always carries the association-not-causation caveat; treat every r as alignment in this file, not mechanism.

NameRequiredDescriptionDefault
endNoYYYY-MM-DD
lagNo0 to 3 days. lag 1 pairs metricA on day d with metricB on the following day.
startNoYYYY-MM-DD
metricAYes
metricBYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint and idempotentHint annotations, the description discloses important quirks: r is withheld below 10 aligned pairs, the function returns both means, and the answer always carries an association-not-causation caveat. It also clarifies lag orientation clearly. This is exactly the kind of behavioral information an agent needs and cannot infer from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three concise, information-dense sentences. Every sentence contributes: the first defines the operation, the second explains the lag and return values, the third provides the necessary interpretation caveat. There is no filler or repetition of schema content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only correlation tool, the description covers the key computation, lag behavior, return fields, sample size threshold, and the contextual caveat. Its main omission is clear default behavior for unlimited or unspecified start/end windows, and it does not describe how alignedPairs is structured. Given there is no output schema, slightly more return-format exposure would make it complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds useful meaning beyond the schema: it explains lag direction, default value, and relationship between aligned daily values. It also identifies the roles of metricA and metricB. Schema coverage is about 60%, and the description compensates for the missing metricA/metricB descriptions, though it does not elaborate on start/end bound semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific operation with a clear verb and resource: 'Pearson correlation between two metrics' daily values.' This is distinct from sibling tools like get_trends or compare_periods because it focuses on pairwise correlation rather than trend direction or period comparison.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The correlation intent is clear, and the lag semantics are explicitly explained, but the description does not state when to prefer this over alternatives like get_trends, compare_periods, or list_metrics. The decision context is implied rather than explicit, and no exclusions or alternative tools are named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_cycle_contextA
Read-onlyIdempotent

Cycle context derived from user-logged period starts (health-cycles.json): day-in-cycle and a coarse phase label (follicular from day 1 to the observed midpoint of that cycle, luteal for the remainder). Derived from logged periods only, never predictive, and no ovulation estimate is made. Optional {date} for a single day; default returns per-day context for the last cycle.

NameRequiredDescriptionDefault
dateNoYYYY-MM-DD

TDQS

A4.7/5.0
Behavior5/5

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 detail beyond those: the phase-label rule, the derived-not-predictive nature, and the observed-midpoint method. It also discloses that no ovulation estimate is made, which is important for an agent considering this tool for health data interpretation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a compact, information-dense two-sentence definition. It front-loads the core purpose and source, then adds necessary behavior constraints and parameter behavior. Each sentence earns its place with no filler, though the organization could be slightly tighter.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only, zero-required-parameter tool with no output schema, the description covers the important context: input semantics, default behavior, derivation source, and the exact output dimensions. The return values are sufficiently described at a high level (day-in-cycle and phase label) to make the tool usable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100% for the single date parameter, and the description enriches it further: the date is optional, it applies to a single day, and the default behavior returns per-day context for the last cycle. This gives the agent a complete understanding of parameter behavior beyond the bare YYYY-MM-DD schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific resource (user-logged period starts), a specific output (day-in-cycle and phase label), and the source file (health-cycles.json). It also distinguishes itself by explicitly noting it is derived from logged periods only and never predictive, which separates it from potential ovulation or prediction tools even without naming them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a clear context for use: it is for cycle context based purely on logged period starts, and it explicitly excludes predictive or ovulation-estimation use cases. It does not explicitly name sibling tools or provide direct when-to-use-alternative language, but the scope and exclusions are strong enough to guide an agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_freshnessA
Read-onlyIdempotent

How current the exported files are, so you can decide whether to trust what you read or ask the user to refresh. stale is judged on the core files (the daily cache and health-sessions.json), never on the hourly intraday file: true when their newest write is older than stale_after_hours (default 26, or HEALTH_STALE_AFTER_HOURS, or your maxAgeHours), or when this machine holds a stale copy. state is fresh, stale, stale_copy, no_data, locked or demo. stale_copy means the phone's manifest (.health-manifest.json) or iCloud says the phone wrote a newer version of a core file than the one on this machine (stale_copy lists each file with localWrittenAt, phoneWrittenAt and reason); relay the recommendation, which says how to make iCloud Drive download it. Also returns age_hours, as_of (when the phone wrote the daily cache), last_data_date, data_lag_days, per-file write times with each file's match against the manifest, stale_files, manifest_behind (the manifest lags the data, not stale), sessions_last_night (present or absent; without a manifest, advisory only, people skip nights) and, on a Mac, an advisory icloud status per core file. Same rule as the status --max-age cron gate. When no export is found, recommendation also names the companion iOS app's App Store page and website (the same links as app.appStoreUrl and app.unlockUrl); when the newest readable export is older than the server's own window (not a stricter maxAgeHours), it names the website. Both carry fixed campaign labels. Cheap: call it again before a long analysis.

NameRequiredDescriptionDefault
maxAgeHoursNoTreat the data as stale when the newest write is older than this many hours (greater than 0, at most 8760). Default 26, or HEALTH_STALE_AFTER_HOURS when set.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes far beyond the readOnly/idempotent annotations: it defines exactly how staleness is judged (core files only, never the intraday file), the threshold precedence (default 26, HEALTH_STALE_AFTER_HOURS, maxAgeHours), and every state value including the non-obvious stale_copy and manifest_behind cases. It also discloses side-adjacent behavior such as embedded campaign-labeled links and the 'advisory only' caveat for sessions_last_night.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded, but the body is a dense single paragraph that mixes threshold rules, state enumeration, return-field inventory, and link/campaign behavior. Every clause is arguably informative, yet the lack of structure makes it harder to scan than it needs to be.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates by naming the returned fields (state, age_hours, as_of, last_data_date, data_lag_days, stale_files, manifest_behind, sessions_last_night, icloud) and explaining the ambiguous ones. Edge cases such as no export found and manifest-behind-data are covered, leaving nothing essential for a correct call unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning the schema does not: the precedence chain between maxAgeHours, the 26-hour default, and the environment variable, plus the note that the rule mirrors the `status --max-age` cron gate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific, agent-relevant purpose: reporting how current the exported files are so the agent can decide whether to trust results. This is unmistakably distinct from sibling read tools like get_structured_export or query_health_data, and the follow-on rules elaborate on the same single job rather than blurring it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit decision context ('decide whether to trust what you read or ask the user to refresh') and a concrete reuse instruction ('Cheap: call it again before a long analysis'). It stops short of naming alternatives or explicit when-not conditions, but the intended call pattern is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_health_metricsA
Read-onlyIdempotent

Get values for a metric (or all metrics) over an optional date range, with an aggregate (avg/sum/min/max/latest). The core data-retrieval tool. Every result carries a coverage block giving the metric's real firstDate/lastDate/days: check it before trusting a long window, and note that aggregate is always computed over the full range even when points are rolled up. Single-metric answers also list any logged point events inside the window as segmentBoundaries. For blood_pressure_systolic or blood_pressure_diastolic at day granularity, the individual readings behind the daily mean are attached as readings [{t, systolic, diastolic, source, manual}] when the app exported them (health-readings.json), the most recent 150 in range, with readingsOmitted counting the rest. A day the app rebuilt after samples arrived late (or were deleted) carries recomputed_at, and a metric with rebuilt or possibly stale past days carries a recompute block (backfilled_days, recomputed_at, capped); untouched days carry neither.

NameRequiredDescriptionDefault
endNoYYYY-MM-DD
limitNoMaximum data points to return (default 365, max 3000). The server rolls up rather than truncating.
startNoYYYY-MM-DD
metricNoMetric name, e.g. step_count, heart_rate, sleep_analysis. Omit for all.
filterDaysNoRestrict to days covered by matching logged events (health-events.json and health-events-agent.json): endDate ranges cover every day inclusive, point events cover their single day, negate:true keeps only days NOT covered. The answer states how many days matched. Example: HRV on night-shift blocks vs days off.
aggregationNo
granularityNoRoll daily values up before returning them. 'auto' (default) picks the finest granularity that fits the response budget, so a multi-year range returns monthly points instead of thousands of daily ones.

TDQS

A3.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare read-only/idempotent; the description adds substantial behavior the agent could not infer: coverage block with firstDate/lastDate/days, aggregate always computed over the full range even when points are rolled up, segmentBoundaries for single-metric answers, readings capped at 150 with readingsOmitted, and recomputed_at/recompute blocks for backfilled days.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in the first two sentences, which is good. But the remainder is a dense run-on covering coverage, aggregations, segmentBoundaries, blood-pressure readings, recompute blocks in one paragraph — the readings/recompute detail could be tightened or split so the calling-critical facts stand out.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description correctly carries the return-shape burden and does so thoroughly for a 7-param tool with a nested filterDays object. It stops short of describing empty-range behavior or what happens when filterDays matches zero days, which a data-retrieval tool's consumers would want.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is high (86%), so the baseline is 3; the description earns above that by clarifying that aggregate is computed over the full range regardless of rollup, and that multi-year ranges degrade to monthly points rather than truncating. It adds little on filterDays or metric naming beyond the schema, but the rollup/aggregate semantics are genuinely new.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

First sentence states a specific verb and resource — get metric values over an optional date range with an aggregate — and the second sentence positions it as 'the core data-retrieval tool'. It never names or contrasts with plausible siblings such as query_health_data, get_trends, get_intraday, or get_structured_export, so an agent can't distinguish them from this text alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: 'check coverage before trusting a long window' and 'use single-metric to get segmentBoundaries' are hints rather than explicit when/when-not rules. No alternative tool is named, and there is no statement about when to prefer query_health_data or compare_periods over this.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_intradayA
Read-onlyIdempotent

The current hour-by-hour window from the iOS app's HOURLY automations (health-intraday.json, app 1.4+): each metric's hourly points (t, v = the hour's value) plus its latest value. The window covers a 48-hour span, yesterday 00:00 local to now, and the file is REPLACED on every run, so this is a live recent view, not history; use get_health_metrics for day-level questions. Discrete metrics (heart rate, SpO2, respiratory rate, HRV, resting heart rate) carry the hour's Min and Max beside v (the hour's mean) when the app version writes them; older exports cover today only and have no Min/Max. Returns available:false with setup guidance when no hourly automation has delivered yet.

NameRequiredDescriptionDefault
metricNoMetric name to filter, e.g. heart_rate. Omit for every metric in the window.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only/idempotent/closed-world, but the description adds material behavior the agent cannot infer: the file is REPLACED on every run, the window spans yesterday 00:00 to now (48h), older exports cover today only with no Min/Max, and discrete metrics pack hour Min/Max beside the mean. These are exactly the non-obvious traits that change how a result should be read.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core payload and tightly packed with no filler, but the clauses chain into long, semicolon-heavy sentences that take effort to parse. Slightly long for one resource, though essentially every clause carries distinct information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description does the work of describing the return shape (t, v = hour's value, Min/Max for discrete metrics) and the degraded/empty responses. Combined with the window and replacement semantics, an agent has enough to call and interpret results correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter's own description already says 'Omit for every metric in the window.' The description implies filtering via 'each metric's hourly points' but adds no syntax, naming convention, or resolution guidance beyond the schema (e.g. use resolve_metric for names). Baseline 3 applies when the schema carries the load.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb/resource ('hour-by-hour window' of 'hourly points' with latest value) sourced from named automations (health-intraday.json, app 1.4+). It explicitly distinguishes itself from get_health_metrics, the day-level sibling, so an agent can route without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names the alternative ('use get_health_metrics for day-level questions') and the condition that selects it, plus the boundary case that this is a live recent view, not history. It also describes the empty-state path ('Returns available:false with setup guidance when no hourly automation has delivered yet'), which tells the agent how to interpret a failure.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_mcp_statusA
Read-onlyIdempotent

Health check: data source, how many metrics/workouts are available, which optional context files exist, the most recent data date, a compact freshness block (its state is stale_copy, with a stale_copy list, when this machine holds an older copy of the export than the phone last wrote), and a recompute summary of past days rebuilt after late-arriving samples (with any days too old to rebuild; supported: null when the cache cannot tell). Call this first to confirm the bridge is connected. The result also includes app.unlockUrl and app.appStoreUrl, the website and App Store page of the companion iOS app that writes the export; both links carry fixed campaign attribution labels.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint/idempotentHint already covering safety, the description adds meaningful return semantics: what stale_copy means, that supported is null when the cache cannot tell, and that the app links carry fixed campaign attribution. It stops short of describing what a disconnected bridge looks like in the response, which is the key thing the 'call this first' advice is meant to detect.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The lead 'Health check:' is well front-loaded, but the rest is one long run-on sentence with nested parentheticals that packs freshness, recompute and URL attribution into a single breath. Every clause adds information, yet the structure makes it harder to scan than a short bulleted list would.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description must carry return-value meaning, and it does so for the freshness and recompute blocks plus the app links. What is missing is the failure/disconnected state, the one behavior an agent calling this as a pre-flight check most needs to interpret.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline for a no-arg tool applies. The description correctly asks for no inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Health check') and enumerates exactly what the result contains: data source, metric/workout counts, context files, latest data date, freshness block, recompute summary and app links. It does not, however, distinguish itself from the sibling get_freshness, which appears to cover overlapping freshness information.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Call this first to confirm the bridge is connected' gives an explicit trigger and ordering, which is strong guidance for the agent. It never says when to prefer the sibling get_freshness instead, so the overlap with that tool is left unresolved.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_profileA
Read-onlyIdempotent

The context fields the user explicitly opted in to sharing (health-profile.json): conditions, medications, goals, allergies, notes. Returns the fields plus a presentFields list. An absent field was withheld by the user or never enabled; absence must never be read as "none".

NameRequiredDescriptionDefault

No parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations establish read-only and idempotent behavior, and the description genuinely adds behavioral value: it discloses the presentFields list and, crucially, warns that an absent field must not be interpreted as a negative value. This semantic caveat goes well beyond annotation signals and helps the agent avoid a dangerous misinterpretation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each earning its place: the resource scope, the return shape, and the semantic caveat. No fluff, no redundancy, minimum well-organized and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no params and no output schema, the description correctly explains both what the returned fields are and what the extra presentFields list means. The 'absent does not equal none' caveat closes the main path for user surprise/misuse. Nothing required to call or interpret this tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes no parameters, so there is no schema coverage burden. The description correctly focuses on the return payload, which is the only remaining semantic surface. It receives a baseline 4 for handling the zero-parameter case well.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific action ('get') and a clear resource ('health-profile.json' shared context fields), and distinguishes that resource from sibling tools by spelling out exactly what data is returned (conditions, medications, goals, allergies, notes). No ambiguity about what the tool operates on.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly defines the narrow scope—context fields explicitly opted into sharing—so an agent can infer when this tool is relevant. However, it does not state when not to use it or explicitly contrast it with sibling tools such as get_health_metrics or get_cycle_context. Usage is implied rather than spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_sleep_sessionsA
Read-onlyIdempotent

Clustered sleep sessions from health-sessions.json with {start, end, day} filters. Sessions are attributed to the WAKING day, matching the daily cache's sleep_analysis, so the two surfaces never disagree; a split night appears as separate sessions with the same day. Duplicate copies of one night left by older app versions (sharing an end time with, or lying inside, a longer session on the same day) are collapsed to the longer one and counted in collapsedDuplicates. A session with manualInBed:true was built from an In Bed entry typed into the Health app with no asleep sample. Timestamps carry the local UTC offset at the time of the sample.

NameRequiredDescriptionDefault
dayNoA single waking day, YYYY-MM-DD.
endNoLast waking day (inclusive), YYYY-MM-DD.
startNoFirst waking day (inclusive), YYYY-MM-DD.

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite annotations covering the read-only/idempotent profile, the description adds real behavioral context: duplicate nights from older app versions are collapsed into the longer session and counted in `collapsedDuplicates`, split nights appear as separate sessions sharing a day, and timestamps carry the local UTC offset. These are non-obvious output behaviors an agent could not infer from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose and filters, then layered with behavioral detail. It is dense and mostly earns its sentences, though it reads as a run of separate caveats rather than a structured hierarchy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description must carry return-value semantics, and it does: collapsedDuplicates, separate sessions for split nights, manualInBed:true provenance, and timezone-offset timestamps. An agent has enough to interpret results correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, and the description earns extra credit by explaining that filters operate on the WAKING day and why split nights share a day. That adds interpretive meaning beyond the schema's terse 'A single waking day' phrasing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (clustered sleep sessions from health-sessions.json) and its filters {start, end, day}, which is more precise than the name alone. It does not name or differentiate against siblings like query_health_data or get_intraday, so an agent gets the what but not the routing signal.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It implies the tool's niche by explaining the waking-day attribution and that it matches the daily cache's sleep_analysis, but there is no explicit 'use this when / use X instead' guidance for choosing between this and the other health-data tools. Usage is inferred rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_structured_exportA
Read-onlyIdempotent

Return clean structured JSON for the chosen metrics/date range. Paginated: the result carries nextCursor when more metrics remain; pass it back as cursor for the next page. Prefer naming the metrics you need and a date range; calling it bare over a full history is a lot of data.

NameRequiredDescriptionDefault
endNoYYYY-MM-DD
limitNoMaximum data points to return (default 365, max 3000). The server rolls up rather than truncating.
startNoYYYY-MM-DD
cursorNoOpaque cursor from a previous call's nextCursor.
metricsNo
granularityNoRoll daily values up before returning them. 'auto' (default) picks the finest granularity that fits the response budget, so a multi-year range returns monthly points instead of thousands of daily ones.

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses pagination behavior, including nextCursor/cursor, and warns about large results. Since annotations already mark this as read-only and idempotent, no contradiction exists and no hidden mutation or side effect is suggested.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each with a distinct purpose: describing what it returns, explaining pagination, and providing usage advice. There is no redundant wording or unnecessary context.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description should have provided a clearer picture of the returned JSON shape; it only says 'clean structured JSON' and mentions nextCursor. Pagination, date range, and metrics guidance are covered, but the actual output format remains under-specified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema property descriptions cover most parameters, so the baseline is already appropriate. The description does not add much semantic value beyond the schema: cursor behavior, limit, and granularity are already described in the input schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence clearly describes the tool's deliverable: clean structured JSON for chosen metrics and a date range. It is distinct enough from analytics-oriented siblings like get_trends or compare_periods, though it does not explicitly name an alternative or scope how it differs from query_health_data.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives concrete usage guidance: name metrics and a date range, and avoid bare calls over a full history because they return a lot of data. It provides a clear practical context but does not explicitly say when to choose this tool over a sibling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_workoutsA
Read-onlyIdempotent

Query the workouts cache with {activityType (a name like Running, or a raw HealthKit id like 37), start, end} filters and pagination ({limit} default 50 max 200, {cursor} from a previous nextCursor). Records are returned exactly as stored, including the newer optional keys (avgHeartRate, maxHeartRate, running dynamics, cycling power, intervals, hasRoute) when the app exported them; older caches simply lack those keys and nothing is fabricated. Also returns summary {count, byActivityType} over everything that matched.

NameRequiredDescriptionDefault
endNoYYYY-MM-DD
limitNoWorkouts per page (default 50, max 200).
startNoYYYY-MM-DD
cursorNoOpaque cursor from a previous call's nextCursor.
activityTypeNoActivity name (e.g. Running) or raw HealthKit activity id (e.g. 37).

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark it read-only and idempotent, and the description adds meaningful behavioral detail: records are returned exactly as stored, optional newer keys may be absent, nothing is fabricated, and the summary applies over all matches. This is precisely the extra non-obvious behavior agents benefit from.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long enough to capture filters, pagination, caching behavior, optional fields, and summary semantics, but every clause contributes. The core query action and paging semantics are front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-parameter read-only tool with no output/output schema, this description covers the important call/response semantics: which filters are allowed, how pagination works, what shape the records take, and what the summary provides. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already describes all five parameters with examples, defaults, and format. The description restates them compactly and adds only marginal grouping/narrative value, so it earns the baseline 3 for a schema with high coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a concrete action ('Query'), a specific resource ('the workouts cache'), and enumerates filters and pagination. The scope is clearly workout data, which distinguishes it from sibling metric/trend/period tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context: this is for querying raw cached workout records within date/activity/filter bounds with pagination. It does not explicitly name alternatives or when not to use it, so it falls short of a full 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_eventsA
Read-onlyIdempotent

Logged context events: medication starts, habit changes, doctor visits, life events, shift blocks, episodes, travel, and any type a newer app adds. Merges the app's health-events.json with health-events-agent.json (events logged with add_event); every event carries source "app" or "agent". Optional {type, tag, start, end} filters; a range event matches a window it overlaps. Sorted ascending by date. Returns available:false when neither file exists; absence means nothing was logged or exported, not that nothing happened.

NameRequiredDescriptionDefault
endNoYYYY-MM-DD
tagNoMatch events carrying this tag.
typeNoEvent type, e.g. medication, habit, visit, life, shift, episode, travel, other. Unknown types pass through and can be filtered by their stored string.
startNoYYYY-MM-DD

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnly/idempotent annotations: discloses the merge of health-events.json with health-events-agent.json, that every event carries a source of "app" or "agent", the ascending sort order, and the available:false shape when neither file exists. That is exactly the kind of provenance and empty-state context annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads what the tool returns, then provenance/merge, then filters, then ordering, then the empty-state caveat. Dense but every clause carries information an agent needs; nothing is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the return-value burden and does so: source field, sort order, and available:false. Combined with the filter overlap rule and the empty-result interpretation, an agent has everything needed to call and interpret it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real semantics: the optional {type, tag, start, end} set is described as filters and the range rule 'a range event matches a window it overlaps' explains behavior the schema does not. It does not detail date format or default window, but the schema already supplies YYYY-MM-DD.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb (list) plus the concrete resource: logged context events across a named set of types. The enumerations (medication starts, habit changes, doctor visits, shift blocks...) make it unmistakable, and it explicitly distinguishes its contents from the sibling add_event by noting agent-logged events.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains filter semantics and the overlap rule, and gives the important interpretive caveat that an empty result means nothing was logged/exported rather than nothing happened. It does not explicitly route the agent against alternatives like query_health_data or get_structured_export, so it falls short of full when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_metricsA
Read-onlyIdempotent

List every available Apple Health metric with its unit, day count, and date range. Use this to discover metric names before querying.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark this as readOnly and idempotent, so no safety contradiction exists. The description adds useful context about the listing including unit, day count, and date range, which goes beyond raw annotation data.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The main action and return content are front-loaded, and the usage guidance is immediately actionable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter discovery tool, the description fully covers what the agent needs: what will be listed, what fields are included, and when to use it. Sibling tool context supports the discovery-to-query flow, and nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so parameter semantics are inherently simple. The description clarifies what the returned listing contains, which is useful given no output schema is present.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies a specific action (list), a clear resource (every available Apple Health metric), and the exact information included (unit, day count, date range). It clearly distinguishes this as a discovery tool from sibling querying tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool: 'discover metric names before querying.' This gives clear usage context and implies this is a prerequisite step rather than an analysis tool, though it does not explicitly name alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

query_health_dataA
Read-onlyIdempotent

Natural-language convenience: pass a question and get routed structured results. Prefer the specific tools above when you can, and call list_metrics first to see how much history exists, since this tool answers over whatever the file holds.

NameRequiredDescriptionDefault
questionYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover readOnlyHint=true, openWorldHint=false, and idempotentHint=true, so the read-only and idempotent nature is established. The description adds one important behavioral fact: results are scoped to whatever data is currently in the file, and users should check list_metrics for the amount of history available. It doesn't explain routing behavior in detail (e.g., how ambiguity is handled), but the additional context about the underlying file scope is useful.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient passage that front-loads the core purpose and then adds practical usage guidance. No words are wasted; the routing concept, the preference for alternatives, and the prerequisite call are all packed into two sentences.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

In the context of a simple one-parameter tool with handers that mark it read-only and idempotent, the main missing pieces for an agent would be the shape of the structured results and any question formatting limits. The instructions to check list_metrics and set expectations on available history partially fill the context gaps, and the description does not need to explain return values due to a lack of an output schema summary line. Still, the absence of any return-format hint keeps it from a 5, but against the overall simplicity it's fairly complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for parameter meaning. The description does explain the question parameter's role by saying pass a question and get routed structured results, which is some semantic content. But it doesn't give examples, constraints, or format guidance for a well-formed question, leaving the parameter meaning incomplete despite a 0% schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that this tool takes a natural-language question and returns routed structured results, and identifies it as a convenience tool with a specific purpose. It also explicitly distinguishes it from the specific tools by referring to them as alternatives, which helps differentiate it from siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says to prefer specific tools when possible, and instructs calling list_metrics first to check the data history before using this tool. This gives an explicit usage context and a clear prerequisite. It doesn't list specific alternatives by name or state when-not conditions in detail, but the guidance provided is concrete and actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

remove_eventA
DestructiveIdempotent

Remove an event that was logged with add_event (id starting "agent-", source "agent" in list_events) from health-events-agent.json. Events logged in the iOS app are refused: they are edited in the app. Removing an id that is already gone changes nothing and says so. Refused in demo mode and when the folder is not writable.

NameRequiredDescriptionDefault
idYesThe agent event id from list_events or add_event.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructive, non-readonly, idempotent, and closed-world, but the description adds real context beyond them: the target file, the source-restriction refusal path, the demo/write-permission refusals, and the observable no-op message when an id is already gone. This is unusually complete for a destructive tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and target file, and every sentence carries actionable constraint or refusal information. The prose is dense and the parenthetical provenance note is slightly compressed, but there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a destructive, idempotent, single-parameter tool with no output schema, the description covers target storage, eligibility, refusal conditions, and no-op behavior. An agent has enough to call it correctly and predict failure modes.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single id parameter already has 100% schema description coverage, so baseline is 3. The description adds provenance semantics by tying accepted ids to add_event/list_events and identifying agent-prefixed ids, which is useful validation context beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (remove an event) plus the exact backing store (health-events-agent.json) and the provenance link to add_event. It is unmistakable against siblings like add_event and list_events.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says which events are removable (agent-logged ids, source 'agent' in list_events) and which are refused (iOS app events), and adds preconditions: demo mode and non-writable folders are refused. That is a clear when/when-not contract with the alternative behavior named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resolve_metricA
Read-onlyIdempotent

Look a metric name up in the canonical naming and unit table. Accepts the export's own name (step_count), a HealthKit identifier (StepCount, HKQuantityTypeIdentifierStepCount), a display title (Step Count) or a common alias (steps, hrv, spo2, weight) and returns the canonical metric, its unit (percentages are 0 to 1 fractions), the unit variants other apps use with the exact formula into the canonical unit, and whether this export holds it. Pass {value, unit} to convert one value, e.g. 180 lb to kg. A word naming several metrics (distance, calories) comes back ambiguous with candidates, and a different measure (RMSSD vs the SDNN that Apple Health stores) is refused with the reason: nothing is guessed. get_health_metrics, get_trends, compare_periods, get_structured_export and correlate_metrics accept the same aliases and report resolvedFrom when they used one; other tools take the export's own names. The export holds one value per metric per day, merged across sources by Apple Health, so this tool states honestly whether per-source disagreement can be seen in the file (exports that record per-source attribution carry src/by and a sources block; others report it as not available) rather than inventing it.

NameRequiredDescriptionDefault
nameYesAny spelling of a metric, e.g. steps, StepCount, Body Weight, VO2 max.
unitNoUnit of value, e.g. lb, mmol/L, degF, km, %. Unknown units are refused, never guessed.
valueNoOptional value to convert into the canonical unit. Pass with unit.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnly, idempotent), but the description goes well beyond them: percentages are 0-to-1 fractions, unit variants come with exact conversion formulas, ambiguous multi-metric words return candidates, and a different measure (RMSSD vs SDNN) is refused rather than guessed. It also discloses that per-source attribution is only present when exports carry src/by, which is genuine behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and accepted inputs are front-loaded and every sentence carries information, but the closing sentence about source attribution and merged-across-sources semantics is long and somewhat peripheral to the lookup job, making the whole denser than necessary.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, yet the description fully describes what comes back (canonical metric, unit, unit variants with formulas, whether the export holds it) plus conversion, ambiguity and refusal behavior. An agent has everything needed to select and invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3; the description still adds value by explaining the conversion pairing ('Pass {value, unit} to convert one value, e.g. 180 lb to kg') and the refusal rule for unknown units, beyond what the schema states. It does not add format constraints on `name` that the schema lacks.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a precise verb and resource: 'Look a metric name up in the canonical naming and unit table.' It then enumerates accepted input forms and return contents, making it clearly distinguishable from siblings like list_metrics or get_health_metrics without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It names which siblings accept the same aliases (get_health_metrics, get_trends, compare_periods, get_structured_export, correlate_metrics) and notes that other tools require the export's own names, which tells the agent when resolution is needed. It stops short of an explicit directive ('call this first when the name is ambiguous'), so it is clear context rather than full when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv1.8.0
    • Addedadd_event
    • Changedcompare_periods1 field changed
      • changedInput schema / properties / anchor / description
        Previous value: -"Build both periods around a logged event instead of passing dates: periodA is the {days} days before the event date, periodB the {days} days after, the event day itself excluded from both. Get event ids from list_events. Pass either anchor or periodA/periodB, not both."New value: +"Build both periods around a logged event instead of passing dates: periodA is the {days} days before the event date, periodB the {days} days after, the event day itself excluded from both. Get event ids from list_events (app and agent-logged events both work). Pass either anchor or periodA/periodB, not both."
    • Changedget_health_metrics1 field changed
      • changedInput schema / properties / filterDays / description
        Previous value: -"Restrict to days covered by matching logged events (health-events.json): endDate ranges cover every day inclusive, point events cover their single day, negate:true keeps only days NOT covered. The answer states how many days matched. Example: HRV on night-shift blocks vs days off."New value: +"Restrict to days covered by matching logged events (health-events.json and health-events-agent.json): endDate ranges cover every day inclusive, point events cover their single day, negate:true keeps only days NOT covered. The answer states how many days matched. Example: HRV on night-shift blocks vs days off."
    • Addedremove_event
  2. 2 tool updatesv1.6.3
    • Addedget_freshness
    • Addedresolve_metric
  3. 10 tool updatesv1.4.0
    • Changedcompare_periods3 fields changed
      • addedInput schema / properties / anchor
        Added value: +{
        +  "description": "Build both periods around a logged event instead of passing dates: periodA is the {days} days before the event date, periodB the {days} days after, the event day itself excluded from both. Get event ids from list_events. Pass either anchor or periodA/periodB, not both.",
        +  "properties": {
        +    "days": {
        +      "description": "Days on each side of the event.",
        +      "type": "integer"
        +    },
        +    "eventId": {
        +      "description": "Event id from list_events.",
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / excludeTravelDays
        Added value: +{
        +  "description": "Drop days on which a timezone change landed (health-days.json): those days were not 24 hours long, so their totals are stretched or shortened by the clock. The answer reports how many days were excluded and why.",
        +  "type": "boolean"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "metric",
        -  "periodA",
        -  "periodB"
        -]New value: +[
        +  "metric"
        +]
    • Addedcorrelate_metrics
    • Addedget_cycle_context
    • Changedget_health_metrics1 field changed
      • addedInput schema / properties / filterDays
        Added value: +{
        +  "description": "Restrict to days covered by matching logged events (health-events.json): endDate ranges cover every day inclusive, point events cover their single day, negate:true keeps only days NOT covered. The answer states how many days matched. Example: HRV on night-shift blocks vs days off.",
        +  "properties": {
        +    "eventTag": {
        +      "description": "Event tag to match, e.g. nights.",
        +      "type": "string"
        +    },
        +    "eventType": {
        +      "description": "Event type to match, e.g. shift, medication, travel.",
        +      "type": "string"
        +    },
        +    "negate": {
        +      "description": "Keep only days NOT covered by matching events.",
        +      "type": "boolean"
        +    }
        +  },
        +  "type": "object"
        +}
    • Addedget_intraday
    • Addedget_profile
    • Addedget_sleep_sessions
    • Changedget_trends1 field changed
      • addedInput schema / properties / excludeTravelDays
        Added value: +{
        +  "description": "Drop days on which a timezone change landed (health-days.json): those days were not 24 hours long, so their totals are stretched or shortened by the clock. The answer reports how many days were excluded and why.",
        +  "type": "boolean"
        +}
    • Addedget_workouts
    • Addedlist_events
  4. 2 tool updatesv1.2.2
    • Changedget_health_metrics2 fields changed
      • addedInput schema / properties / granularity
        Added value: +{
        +  "description": "Roll daily values up before returning them. 'auto' (default) picks the finest granularity that fits the response budget, so a multi-year range returns monthly points instead of thousands of daily ones.",
        +  "enum": [
        +    "auto",
        +    "day",
        +    "week",
        +    "month",
        +    "quarter",
        +    "year"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / limit
        Added value: +{
        +  "description": "Maximum data points to return (default 365, max 3000). The server rolls up rather than truncating.",
        +  "type": "integer"
        +}
    • Changedget_structured_export3 fields changed
      • addedInput schema / properties / cursor
        Added value: +{
        +  "description": "Opaque cursor from a previous call's nextCursor.",
        +  "type": "string"
        +}
      • addedInput schema / properties / granularity
        Added value: +{
        +  "description": "Roll daily values up before returning them. 'auto' (default) picks the finest granularity that fits the response budget, so a multi-year range returns monthly points instead of thousands of daily ones.",
        +  "enum": [
        +    "auto",
        +    "day",
        +    "week",
        +    "month",
        +    "quarter",
        +    "year"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / limit
        Added value: +{
        +  "description": "Maximum data points to return (default 365, max 3000). The server rolls up rather than truncating.",
        +  "type": "integer"
        +}
  5. 7 tool updatesv1.0.0
    • First observedcompare_periods
    • First observedget_health_metrics
    • First observedget_mcp_status
    • First observedget_structured_export
    • First observedget_trends
    • First observedlist_metrics
    • First observedquery_health_data

TDQS

A4/5.0

Scored across 18 tools

Disambiguation3/5

Most tools target distinct resources or analyses, but several retrieval tools overlap: get_health_metrics, get_structured_export, and query_health_data all return metric data over a date range, and get_mcp_status includes freshness information also covered by get_freshness. The descriptions help clarify preferred use cases, but an agent still has to choose carefully between these pairs.

Naming Consistency5/5

The tool set consistently uses snake_case verb_noun names: get_*, list_*, add_event, remove_event, query_health_data, correlate_metrics, resolve_metric, and compare_periods. The pattern is predictable throughout, with no mixed conventions.

Tool Count4/5

Eighteen tools are slightly above the typical compact range but reasonable for a health-data bridge covering metrics, workouts, sleep, events, freshness, profile, and cycle context. Each tool has a defensible role, though the set could be tightened by consolidating overlapping retrieval and freshness tools.

Completeness4/5

The surface is broad and well covered: metric discovery and resolution, day-level and intraday retrieval, trends, period comparison, correlation, workouts, sleep sessions, cycle context, profile, freshness/status, and event logging. Minor gaps remain, such as no update_event operation and limited workout-route detail, but core workflows are complete.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers