Skip to main content
Glama
Fino-wind

Vaultbeat MCP Server

Vaultbeat Apple Health MCP Server

Your Apple Health data — sleep stages, cycle, HRV, resting heart rate, workouts, weight, VO₂ max, meals, lifts, notes, symptoms — readable and writable by your own AI agent (Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, anything that speaks MCP), end-to-end encrypted so that only your machine ever sees plaintext.

The Vaultbeat iPhone app reads HealthKit and encrypts every record on the phone. This package is the local server your agent talks to: it downloads the ciphertext, decrypts it on your machine, and serves it over MCP.

iPhone (HealthKit → Vaultbeat app, encrypts) ──► cloud (ciphertext only) ──► this server (decrypts locally) ──MCP──► your agent

The cloud stores ciphertext it cannot read. The key that opens it is generated on your machine when you pair, and never leaves it.

Requirements

  • The Vaultbeat iOS app, 1.2.3 or newer, signed in — App Store

  • uv (for uvx). The server needs Python 3.11+, and uvx downloads a suitable interpreter by itself if your system Python is older.

  • Vaultbeat Pro for agent access. Pairing a machine starts a 3-day trial of the AI interface; after that, reads and writes need Pro, bought in the iOS app (Settings → Membership). When access lapses nothing is deleted, and buying Pro resumes this machine without re-pairing.

How much history this server can read is decided by what the app uploads. With Pro, that is your whole history. A trial uploads your whole history on app 1.2.9 and later, and your last 7 days on 1.2.8 and earlier. Without either, app 1.2.8 and earlier upload your last 7 days and app 1.2.9 and later upload nothing. So a short history is usually a plan boundary, not a sync delay — vaultbeat_doctor tells the causes apart.

Related MCP server: apple-health-shortcuts-mcp

Quick start

1. Pair this machine with your iPhone.

uvx vaultbeat-apple-health@latest bind

It prints a QR code and waits. In the Vaultbeat app open MCP tab → Connect an AI server (app 1.2.8 and earlier: Settings → Data & AI) and scan it. The command waits 5 minutes (--timeout to change); once scanned there are 10 minutes to finish.

2. Add the server to your MCP client.

Claude Code:

claude mcp add vaultbeat-health -- uvx vaultbeat-apple-health@latest serve --transport stdio

Claude Desktop — add to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json) and restart it:

{
  "mcpServers": {
    "vaultbeat-health": {
      "command": "uvx",
      "args": ["vaultbeat-apple-health@latest", "serve", "--transport", "stdio"]
    }
  }
}

Claude Desktop does not inherit your shell's PATH. If it reports that uvx cannot be found, put the absolute path from which uvx in "command".

Any other MCP client: run uvx vaultbeat-apple-health@latest serve --transport stdio as a stdio server. The @latest matters — without it uvx keeps running whichever version it cached first.

3. Check it works. Ask your agent to call vaultbeat_doctor, or run:

uvx vaultbeat-apple-health@latest doctor

It checks the config, the private key, cloud reachability, the pairing and a real fetch-and-decrypt round trip, and lists which data types have nothing to read yet. Right after pairing the phone is still sealing your history for the new machine, so a few kinds may be empty for a while; MCP tab → Re-sync all health data to AI in the app (app 1.2.8 and earlier: Settings → Data & AI) speeds it up.

Try it without an iPhone

uvx vaultbeat-apple-health@latest --demo doctor
uvx vaultbeat-apple-health@latest --demo serve --transport stdio   # wire this into a client

--demo is a global flag — it goes before the subcommand — and serves a fixed synthetic dataset: no pairing, no cloud, no key. Every result says demo_mode: true and carries a [SYNTHETIC DEMO DATA] banner, and the log_* write tools refuse.

MCP tools

26 tools. Reads return your own data — the account that paired this machine. Tools marked partner also take partner=true to read what your partner chose to share with you in their app — sleep, water and weight, and cycle, symptoms and notes only if they turned that on. The two people are never mixed in one result. Every read tool takes fresh=true to skip the local cache.

Diagnostics

Tool

What it does

vaultbeat_doctor

Diagnose this install end to end, report which data types have no data and the likely reasons, and show the pairing state. The tool to call before telling anyone their data is missing.

Health records

Tool

What it returns

get_sleep_nights partner

Every night as one compact row: bed and wake time, asleep, deep / REM / core / awake, awakenings, longest unbroken sleep, heart and breathing rate while asleep, naps. A year fits in one call; since="YYYY-MM-DD" for a calendar window.

get_sleep_detail partner

One or two nights in depth: stage intervals with per-stage heart and breathing rate. The newest nights, or any night by date (since / until).

get_menstrual_cycle partner

Cycle samples and a next-period prediction. Sensitive.

get_symptoms partner

Symptoms from Apple Health, and beside them the episodes a person reported (type, severity, onset and end, place, suspected triggers). Sensitive.

get_notes partner

Free-text notes on days, with who wrote them. Sensitive.

get_strength_log

Strength sessions: exercises, sets × reps, volume per session.

get_food_log

Meals and items per day, with optional kcal / protein / fat / carbs. The 14 newest days that have a log by default (not a calendar fortnight), or a since / until window.

get_workouts

Workouts: type, duration, calories, distance.

get_user_profile

Sex, age, date of birth and height. Needs Vaultbeat for iOS 1.2.9 or later.

Daily numbers and analysis

Tool

What it returns

get_metric partner

Per-day values for one series, several, or all of them: sleep duration, timing and stages, resting HR, HRV, wrist temperature, VO₂ max, weight and body composition, water, steps, active / basal / total energy, exercise and stand minutes, distance, mindfulness. aggregation (avg / sum / min / max / latest) and granularity (day / week / month / weekday) are computed server-side; since / until for a calendar window. Today on an accruing series is marked partial and kept out of aggregates.

get_intraday

Samples within a day, for HRV: one row per hour that has samples (granularity="hourly") or one per sample ("raw"). About 8–14 rows a day; limit counts rows.

list_metric_series partner

Every series name the tools above and below accept, with its unit and how much data backs it.

get_metric_trend partner

Least-squares slope, endpoints, mean / median / min / max.

compare_metric_periods partner

The newest N days against the N before them, or two named windows.

correlate_metric_series partner

Pearson r between two series over the days that have both; lag_days pairs a day with a later one.

The analysis tools return numbers only — no scores, grades or verdicts — and refuse rather than fit a line to two points.

Writes — all into the account that paired this machine, and nowhere else.

Tool

What it does

log_food_append / log_strength_append / log_note_append

Add to a day. Cannot delete anything — the safe default when a day may already have entries.

log_food_entry / log_strength_entry / log_note

Replace that whole day with what is passed, and report exactly what was removed.

log_weight_entry

Record a weigh-in, keeping that day's body composition.

log_symptom

Record one symptom the person reports, as structured fields a later read can match against sleep, food and heart rate.

update_symptom / delete_symptom

Change a reported symptom (usually its end time), or erase one logged by mistake.

log_note and log_note_append take partner=true to record a note about your partner. It stays in your own account, is never sent to them, and is kept apart from your own notes.

Reading the results

  • Every read carries a coverage block. days_covered is how many distinct days the answer rests on — quote it beside any average or trend. more_available: true means older records exist and your limit stopped short of them (oldest_available says how far back they go); it is never a sign of missing data.

  • Results are bounded. A result larger than about 60 KB is not sent; the tool returns result_too_large with a suggestion for narrowing the request (a smaller limit, a since date, one series instead of all). get_metric and get_intraday return their rows as a compact table (columns + rows).

  • Unknown arguments are rejected. A misspelled or retired parameter is an error, never silently ignored.

MCP prompts

The server also serves eight prompts over prompts/list — daily_brief, sleep_review, energy_balance, training_block_review, cycle_aware_read, partner_check_in, log_from_conversation and why_is_this_empty. Each names the tools to call and carries the same two rules: say what the data covers before concluding, and treat an empty result as something to explain (it has several possible causes), never as a zero. Every argument is optional.

Privacy and security

  • Decryption happens only on this machine. The cloud holds ciphertext and per-recipient wrapped keys; the private key that opens them is generated here when you pair.

  • Health data leaves this package only through MCP. The command-line subcommands pair a machine, report on that pairing and start the server; none of them prints health data.

  • Sensitive categories. Your cycle and the symptoms Apple Health records reach this server only after you turn them on, per category, in the iOS app — they are off by default. Your notes and the symptoms you report yourself (in the app, or through log_symptom) are always readable by your own agent. None of these reaches your partner's agent unless you turn on sharing it with them.

  • Unpair at any time from the app's MCP tab (app 1.2.8 and earlier: Settings → Data & AI); the machine can then decrypt nothing new.

  • Tool results never contain the private key or the server token.

Where the private key lives

Not in config.json. It is looked for in this order:

  1. the VAULTBEAT_PRIVATE_KEY environment variable, if set (read, never written back) — for operators injecting it from a secret store;

  2. the system keyring — the normal case on a desktop;

  3. ~/.tether/mcp-local/identity.key, mode 0600 — the automatic fallback on a machine with no keyring.

~/.tether/mcp-local/config.json (mode 0600) holds the cloud-issued server token and your public key. .tether is the app's former name and the path is kept on purpose: existing installs find their key through it.

Never delete config.json to "start clean". The private key is not in it, so deleting does not clear a bad key — it mints a new identity, and every record already encrypted for the old one becomes permanently unreadable. To repair a pairing, run bind again: it re-pairs this machine in place and keeps what is already encrypted for it.

Headless servers. If the keyring is unreachable, let the identity.key fallback handle it — it engages by itself. Do not set PYTHON_KEYRING_BACKEND to the null backend: it accepts writes and stores nothing, so it only hides a keyring you might be able to reach. If a D-Bus session exists but this process cannot see it (common when the server is started by an agent framework, systemd or cron), pass DBUS_SESSION_BUS_ADDRESS through explicitly — resolve it first (echo "unix:path=/run/user/$(id -u)/bus") and put the literal result in the client's env block, since that block is JSON, not a shell.

Telemetry

The server sends usage events to PostHog (EU): which tool was called, whether it succeeded, a duration range, and the AI client and package versions — tied to the account this machine is paired to. Tool arguments, results and health data are never sent. Set VAULTBEAT_TELEMETRY=0 (or DO_NOT_TRACK=1) to turn it off. Details: vaultbeat.app/privacy.

Troubleshooting

Start with doctor. uvx vaultbeat-apple-health@latest doctor prints an [OK] / [FAIL] list with a fix for the first thing that is broken; doctor --json is the same report for an agent. Exit code 0 means healthy.

A read comes back short or empty. Read coverage.more_available first: true means your limit was the boundary — ask for more. false with a short history is the plan boundary described under Requirements, or a pairing that is still filling in. vaultbeat_doctor lists the kinds with no data and the possible reasons for each.

The QR code looks wrong (mojibake or uneven rows — usually Windows with a non-UTF-8 code page). The pairing is still waiting, so do not re-run bind: that replaces the code on screen. Instead render the JSON payload printed just above the code as an image (qrencode -o pair.png '<payload>'), send it to your phone and use import from Photos in the app's scanner; or run chcp 65001 in a fresh terminal; or use bind --no-qr for the payload as text.

Reads fail with an access message. The trial or Pro membership has lapsed. Nothing has been deleted; buying Pro in the iOS app (Settings → Membership) resumes this machine without re-pairing.

HTTP transport

For clients that connect over a network instead of launching a subprocess:

vaultbeat-apple-health serve --generate-token            # mint and store a bearer token, print client config
vaultbeat-apple-health serve --transport http            # 127.0.0.1:8000/mcp, bearer token required

The token is read from VAULTBEAT_MCP_HTTP_TOKEN (preferred, keeps it out of shell history) or the stored config, and clients send it as Authorization: Bearer <token>. --show-token prints the stored one. --no-token serves loopback without auth.

Binding beyond loopback (--host 0.0.0.0 for a LAN or VPS) fails closed: it needs both a token and --allow-remote. The token crosses the wire in clear text, so put TLS in front of it (Caddy, nginx, Cloudflare). Other flags: --sse-response for SSE-style responses, --stateful-http for clients that need sessions.

Example mcp.json (VS Code / Cursor style):

{
  "servers": {
    "vaultbeat-health": {
      "type": "http",
      "url": "http://127.0.0.1:8000/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

A client that only speaks stdio can reach the HTTP server through mcp-remote: npx -y mcp-remote http://127.0.0.1:8000/mcp --header "Authorization: Bearer <token>".

Reporting issues

This repo takes issues for both the MCP server and the Vaultbeat iPhone app — install failures, tool errors, binding that never completes, doctor reporting something wrong, and app problems too (UI, subscriptions, HealthKit permission prompts, sync not showing up on the phone). Open an issue and pick the template that fits. Questions and ideas can go in Discussions.

This is a public repo: never paste health data, pairing codes or account details.

Development

The command line has six subcommands, and none of them reads health data:

vaultbeat-apple-health bind      # pair this machine with the iOS app (QR)
vaultbeat-apple-health status    # local pairing state
vaultbeat-apple-health doctor    # self-diagnosis
vaultbeat-apple-health init      # generate a keypair and config without pairing
vaultbeat-apple-health poll      # poll once for a pending pairing
vaultbeat-apple-health serve     # run the MCP server (stdio or http)

Decrypted records are cached per data type under ~/.tether/mcp-local/cache/ (files 0600, directory 0700) for 600 seconds; VAULTBEAT_MCP_CACHE_TTL overrides it (0 disables). After the first read, a refresh downloads only the records that changed. Pairing to a different server identity clears the cache.

Run the checks from a clone:

uv sync --frozen --all-extras
uv run --frozen pytest -q tests
uv run --frozen ruff check src tests
uv run --frozen mypy src

vaultbeat-mcp and vaultbeat-mcp-local remain as console-script aliases for installs from before the package was renamed vaultbeat-apple-health (0.6.2).

Available Tools

26 tools
compare_metric_periodsCompare two periodsA
Read-onlyIdempotent

Compare two periods of one series: the newest days vs the days before, or two named windows.

To compare CALENDAR periods — "this semester vs the summer", "before and after I started training" — pass period and baseline, each "YYYY-MM-DD..YYYY-MM-DD" (an open end is allowed: "2026-09-01.."). days is then ignored. Without them, the rule below applies.

"Before" means the next-oldest days WITH DATA, so a gap makes the earlier window older rather than emptier — read previous.first_day / previous.last_day to see which period you actually got, and quote them.

Returns both windows' mean/median/min/max and the differences. It does not say which window is better; that depends on the metric and on the person.

Carries a coverage block over the days the arithmetic used: quote coverage.days_covered and coverage.span_days beside any number here. coverage.window_satisfied: false alone does NOT mean a short history — it is also false when older days exist beyond the window; coverage.more_available tells the two apart.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNo
freshNo
periodNo
seriesYes
partnerNo
baselineNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description goes well beyond them: it defines what 'before' means (next-oldest days WITH DATA, so gaps push the window older rather than empty), warns that the tool does not judge which window is better, and explains the ambiguous `coverage.window_satisfied: false` case with `coverage.more_available` as the disambiguator. That is genuine non-obvious behavior disclosure.

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 rule before the calendar-period exception, and every sentence carries information (format, precedence, gap semantics, coverage caveat). It is long, but the length is justified by the tool's conditional logic; minor repetition of 'quote' guidance.

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

Completeness4/5

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

An output schema exists, so explaining return values is not strictly required, yet the description usefully frames them (mean/median/min/max plus differences, coverage block) with interpretation caveats. It is nearly complete for a complex tool, with the only real hole being the undocumented `fresh` and `partner` flags.

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 carry all parameter meaning. It does well for `period`/`baseline` (exact 'YYYY-MM-DD..YYYY-MM-DD' format, open-end allowed) and `days` (ignored when windows are given), but `fresh` and `partner` are completely undocumented in both schema and description, and `series` is only implied. Two of six parameters are opaque, so this is partial compensation.

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 verb and resource ('Compare two periods of one series') and immediately distinguishes the two operating modes (newest `days` vs prior `days`, or two named windows). An agent can tell this apart from get_metric_trend or correlate_metric_series 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?

Explicitly routes the agent: use `period`/`baseline` for calendar-style comparisons ('this semester vs the summer', 'before and after I started training') and fall back to `days` otherwise, with the precedence rule stated ('`days` is then ignored'). No inference required.

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

correlate_metric_seriesCorrelate two seriesA
Read-onlyIdempotent

Pearson r between two daily series, over days that have BOTH recorded.

lag_days pairs series_a on each day with series_b that many days LATER (negative = earlier): "does a short night show up in HRV the day after" is sleep vs HRV with lag_days=1. Sleep is already dated by the morning it ends, so lag 0 is "last night vs today". since / until ("YYYY-MM-DD") restrict to a calendar window.

Days missing on either side are dropped, never interpolated and never read as zero, so n_pairs is usually smaller than either series — quote it with r. Fewer than 3 shared days returns null with a reason: any two points are perfectly collinear, so a coefficient there is an artefact.

The result carries a caveat field about causation. Repeat its substance in your answer, and do not translate r into a word like "strong".

Carries a coverage block over the days the arithmetic used: quote coverage.days_covered and coverage.span_days beside any number here. coverage.window_satisfied: false alone does NOT mean a short history — it is also false when older days exist beyond the window; coverage.more_available tells the two apart.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNo
freshNo
sinceNo
untilNo
partnerNo
lag_daysNo
series_aYes
series_bYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

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

Annotations only declare the safety profile (read-only, idempotent, non-destructive); the description adds real behavioral depth: missing days are dropped rather than interpolated or zero-filled, n_pairs can be smaller than either series, <3 shared days returns null with a reason, and it explains the counterintuitive coverage.window_satisfied=false case. This is exactly the context annotations cannot carry.

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?

Dense and front-loaded: the definition comes first, then lag semantics, then edge cases and output guidance. Every paragraph does work, though the output-handling instruction ('repeat its substance... do not translate r into a word like strong') is a style directive that lengthens the definition beyond strict tool-selection needs.

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

Completeness4/5

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

An output schema exists, yet the description still explains the two fields an agent must reason about (caveat, coverage) and warns against misuse of window_satisfied, which is genuinely useful. It is nearly complete; the unexplained days/fresh/partner parameters are the only real gap.

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

Parameters3/5

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

Schema coverage is 0%, so the description carries the full burden, and it does well for lag_days (including negative semantics and the 'sleep dated by the morning it ends' nuance), series_a/series_b pairing, and since/until format. But days, fresh, and partner are never mentioned, leaving three of eight parameters undocumented anywhere.

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 precise verb+resource: 'Pearson r between two daily series, over days that have BOTH recorded.' That is unambiguous and clearly distinct from siblings like get_metric_trend or compare_metric_periods, which measure trends/period shifts rather than pairwise association.

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 concrete use case and the exact invocation for it ('does a short night show up in HRV the day after' = sleep vs HRV with lag_days=1), plus the condition that produces null (<3 shared days). It does not explicitly name a sibling alternative or state when NOT to use it, which keeps it short of a 5.

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

delete_symptomDelete symptom (erases the entry)A
Destructive

Erase one reported symptom that was logged by mistake (agent write).

Only for an entry that should never have existed — a duplicate, the wrong person, a symptom they did not have. A symptom that simply ended is NOT deleted: record its end with update_symptom(entry_id, end_at=…).

The entry's content is overwritten in the cloud, so it disappears from get_symptoms and from the app. The result carries deleted_entry — the full entry as it was — so a mistaken delete can be logged again with log_symptom. Symptoms imported from Apple Health cannot be deleted here.

ParametersJSON Schema
NameRequiredDescriptionDefault
entry_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

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

Annotations already declare `destructiveHint: true`, `readOnlyHint: false`, and `idempotentHint: false`, so the bar is lower. The description still adds substantial context beyond annotations: the entry is overwritten in the cloud, disappears from `get_symptoms` and the app, and the response carries `deleted_entry` so a mistaken delete can be logged again.

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-loads the core action and then layers conditions and caveats in a logical order. Every sentence adds useful information, though the description is slightly longer than strictly necessary and the parenthetical '(agent write)' is mildly redundant given annotations.

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 destructive single-entry tool, the description covers purpose, usage, alternatives, side effects, and a key restriction. It omits some edge cases such as auth requirements or behavior for a nonexistent `entry_id`, but an output schema exists and annotations already carry the destructive/idempotency profile.

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

Parameters2/5

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

There is one parameter (`entry_id`) with 0% schema description coverage. The description does not define what `entry_id` is, where to obtain it, or its format; it only mentions `entry_id` incidentally in the `update_symptom` alternative. With low coverage, the description should compensate but does not.

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: 'Erase one reported symptom that was logged by mistake.' It also scopes the tool to erroneous entries and explicitly distinguishes it from `update_symptom` and `log_symptom`, so an agent can route correctly without opening sibling schemas.

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?

Gives explicit when-to-use conditions (duplicate, wrong person, symptom they did not have) and an explicit when-not-to-use rule ('A symptom that simply ended is NOT deleted'), naming `update_symptom` as the alternative. It also states a hard limitation: Apple Health imports cannot be deleted here.

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

get_food_logFood logA
Read-onlyIdempotent

Decrypt daily food-intake logs locally (newest first).

limit counts LOGGED days: by default the newest 14 days that have a log — not a calendar fortnight. Days with nothing logged are skipped, so the 14 can reach further back; coverage.span_days and coverage.days_missing_in_span say how far. since / until ("YYYY-MM-DD", local days, both inclusive) read a calendar window instead — "what did I eat in the first week of August" — and then limit applies only if you pass it. Each day's date is its local calendar day (the same as local_date, and the form log_food_entry and get_strength_log use). A logged day is roughly 0.5-1k tokens (more with notes, and Chinese text counts more), so a month can already be past what a client takes in one result — it then comes back as result_too_large; ask for fewer days or a narrower window.

Each day carries meals, each meal a list of items with food (name), optional free-text portion ("1 根" / "300g" / "小份"), optional per-item/per-meal note, and — when the logging agent estimated them — optional structured nutrition numbers (kcal, proteinGrams, fatGrams, carbGrams). Items without those fields need analysis-time estimation from name + portion; items with them can be summed directly. Owner's own days only. Pass limit_days to cap how many days return.

Carries a coverage block: quote coverage.days_covered (distinct days, not the row count) and coverage.span_days beside any average or trend. coverage.window_satisfied: false alone does NOT mean a short history: it is also false when older days exist beyond your window. 🔴 Before saying how far back someone's data goes, read coverage.more_available: true means this server can decrypt days OLDER than first_day that your limit left behind — re-read with a larger limit, or quote coverage.oldest_available as the real start of their history. Never report a limit-shaped window as the extent of their data.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo
sinceNo
untilNo
limit_daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

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

Annotations already declare read-only/idempotent/non-destructive, but the description adds substantial behavior the annotations cannot: token cost per logged day, the `result_too_large` failure mode, filigree of the `coverage` block, and the critical `coverage.more_available` flag that warns against reporting a limit-shaped window as the true extent of history.

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?

Long but front-loaded and largely earned by the tool's complexity and the near-empty schema. Some redundancy remains (two separate day-capping params, repeated coverage guidance), so it is dense rather than wasteful.

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?

Given an output schema exists, return-value explanation is not strictly required, yet the description usefully explains the coverage/meals/items shape and the failure mode. It is nearly complete for a complex 5-param read tool, short only on the undocumented `fresh` parameter.

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

Parameters3/5

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

Schema coverage is 0% across 5 params, so the description must carry the load. It well explains `limit`, `since`/`until` (format and inclusivity), and touches `limit_days`, but `fresh` is never mentioned and `limit` vs `limit_days` is left ambiguous ('cap how many days return'), which is a real gap for a 0%-coverage 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?

Opens with a specific verb+resource: 'Decrypt daily food-intake logs locally (newest first).' An agent immediately knows this is a read of logged meals, distinct from the sibling writers log_food_entry/log_food_append and from get_strength_log which it referencing-crosses by date format.

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 clear in-tool routing: use `limit` for 'newest N logged days' vs `since`/`until` for a calendar window ('what did I eat in the first week of August'), and tells the agent to ask for fewer days on `result_too_large`. It does not, however, compare this tool against any alternative sibling (e.g. get_notes), so no explicit when-not guidance.

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

get_intradayIntraday samplesA
Read-onlyIdempotent

Samples WITHIN a day, for the series that record several per day (HRV).

granularity:

  • "hourly" (default) — one row per hour that HAS samples (UTC hour boundaries, shown in local time), the mean SDNN of that hour and its sample_count. Right for "how did my HRV move through the day".

  • "raw" — one row per sample, newest first.

The Watch measures HRV a handful of times a day, not continuously (more during a Breathe session), so most days have about 8-14 rows either way. limit counts ROWS, not days: the default 168 is roughly two to three weeks, not one. How far back the history goes depends on what this phone uploaded, not on a fixed window — read coverage.oldest_available and coverage.more_available. For one number per day use get_metric. Reads YOUR data only: HRV is never shared between partners.

rows is a table in columns order (local_time, sdnn_ms, plus sample_count for hourly). Carries a coverage block: quote coverage.days_covered (distinct days, not the row count) beside any number.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo
seriesNohrv_sdnn
granularityNohourly

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description adds genuinely non-obvious behavior: HRV is measured sporadically (8-14 rows/day), history length depends on what the phone uploaded rather than a fixed window, and the coverage block must be read for oldest_available/more_available. The only gap is the unexplained `fresh` parameter's effect.

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?

Dense but front-loaded: the core scope lands in the first sentence, then granularity options, then caveats. Every sentence carries information, though the granularity bullet block makes it longer than strictly needed for a read tool.

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

Completeness4/5

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

An output schema exists, yet the description still usefully explains row shape, column order, and the coverage block — appropriate for a data-returning tool. It is nearly complete for an agent's needs, missing only an explanation of what `fresh` does and what it changes.

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 0%, so the description must carry the load, and it does well for three of four parameters: granularity modes are fully defined, limit semantics (rows, default 168 ≈ 2-3 weeks) are clarified, and series is implied as HRV SDNN. `fresh` is never mentioned, leaving one parameter undocumented in both schema and description.

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 (samples) and a precise scope: within-day samples for series that record multiple times per day (HRV). It explicitly distinguishes itself from get_metric ('For one number per day use get_metric'), so an agent can route between the two without reading schemas.

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?

Gives explicit when-to-use guidance for each granularity mode ('hourly' for how HRV moved through the day, 'raw' for per-sample rows) and names the alternative tool for daily aggregation. It also warns that limit counts rows not days, which prevents a common misuse.

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

get_menstrual_cycleMenstrual cycleA
Read-onlyIdempotent

Decrypt recent menstrual cycle data locally and predict the next period.

SENSITIVE: menstrual data only reaches this server if the user explicitly opted in on iOS; it stays on-device and is never re-exported. Returns recent samples plus a next-period prediction. Reads YOUR cycle by default; if the cycle being asked about is your partner's, pass partner=true (it reaches this server only if they share it).

Carries a coverage block: quote coverage.days_covered (distinct days, not the row count) and coverage.span_days beside any average or trend. coverage.window_satisfied: false alone does NOT mean a short history: it is also false when older days exist beyond your window. 🔴 Before saying how far back someone's data goes, read coverage.more_available: true means this server can decrypt days OLDER than first_day that your limit left behind — re-read with a larger limit, or quote coverage.oldest_available as the real start of their history. Never report a limit-shaped window as the extent of their data.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo
partnerNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnly/idempotent/non-destructive, but the description adds substantial context they cannot: the opt-in-only, on-device, never-re-exported sensitivity constraint, plus the return shape (samples + next-period prediction). The detailed `coverage` semantics, including the false-negative trap on `window_satisfied` and the `more_available` re-read instruction, is exactly the kind of behavioral disclosure annotations can't convey.

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 purpose is front-loaded, then structured into sensitivity, routing, and coverage-guidance blocks. It is longer than most definitions, but nearly every sentence (opt-in condition, partner routing, window_satisfied caveat, more_available rule) prevents a specific misinterpretation, so the length is largely earned.

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?

An output schema exists, so return values needn't be enumerated, yet the description still tells the agent how to interpret the `coverage` block correctly. Combined with the privacy and partner-routing context, an agent has everything needed to call this tool and report its results without misreading the window.

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 0%, so the description carries the load, and it does for two of three params: `partner` is explained directly and `limit` is explained through the extended 'limit-shaped window' discussion and the re-read-with-larger-limit instruction. `fresh` is never defined, leaving one undocumented parameter, which keeps this just under full marks.

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 ('Decrypt recent menstrual cycle data locally and predict the next period'), so an agent knows exactly what it returns before opening the schema. It is clearly distinguishable from siblings like get_symptoms or get_sleep_nights because it is the only cycle/period tool.

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?

Explicitly says the tool reads YOUR cycle by default and to pass `partner=true` when the cycle belongs to a partner. That is real routing guidance for the main ambiguity ('whose data'). It does not, however, describe when to prefer this over adjacent tools such as get_metric or get_symptoms, so it stops short of full when/when-not coverage.

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

get_metricDaily health metricsA
Read-onlyIdempotent

Read any one-number-per-day health series: steps, resting HR, HRV, weight, …

series takes ONE name, a LIST of names, or nothing (= every series). Names come from list_metric_series, which also says how much data backs each — call it first rather than guessing. Activity is five series (steps, active_energy, exercise_minutes, stand_minutes, distance_meters); pass them as a list to get the whole day in one call.

days = the newest N days THAT HAVE DATA, per series (not N calendar days). For a CALENDAR window — "last month", "this semester", "July" — pass since / until ("YYYY-MM-DD", either or both) instead; days is then ignored and the reply names the range it used.

aggregation is computed here, not by you: avg / sum / min / max / latest over the window, or none (default) for the per-day points only. sum is refused on state measurements (resting HR, weight, VO2max) — it has no meaning there. granularity = day (default), week (ISO weeks) or month; week/month return buckets aggregated with aggregation (avg when none), each with days_with_data beside days_in_period. period_in_progress: true marks the current, unfinished week/month and clipped_by_window: true the oldest one your days cut through — never compare either against a full period as if it were one. granularity = weekday groups the window by day of the week (Mon..Sun) — the direct answer to "weekends vs weekdays" and to social jet lag. Read its weekday_note: sleep is dated by the morning it ends, so Sat is the Friday-night sleep.

Honesty the reply already does for you, so do not undo it: · On cumulative series (steps, energy, water) today is still accruing — it is marked partial: true and excluded from every aggregate. · excluded_days names days dropped because their data was short (basal energy with the Watch off the wrist, TDEE without basal), with reasons. · Missing days are absent, never zero.

Each series' points (or buckets) is a table: columns names the fields once and every entry of rows is one day (or period) in that order — e.g. columns: ["date", "value", "partial"]. The flags above are columns; a day without one has null there.

Each series carries its own coverage block: quote coverage.days_covered and coverage.span_days beside any number. 🔴 Before saying how far back someone's data goes, read coverage.more_available: true means older days exist that your days left behind — ask for more, or quote coverage.oldest_available. Never report a window as the extent of their data.

Reads YOUR data by default. Pass partner=true for your partner's — of these series only sleep, water and weight (with body composition) can be shared, so every other series comes back empty for a partner by design.

Sleep is here too — duration, timing (bedtime_minutes, wake_minutes, sleep_midpoint_minutes), stages, awakenings, heart/breathing rate while asleep; the nights behind those numbers are get_sleep_nights. Richer kinds have their own tools: workouts, strength, food, notes, symptoms, cycle, and the sex / age / height profile (get_user_profile). Samples within a day (HRV spikes) → get_intraday. Trend / period comparison / correlation → get_metric_trend, compare_metric_periods, correlate_metric_series.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNo
freshNo
sinceNo
untilNo
seriesNo
partnerNo
aggregationNonone
granularityNoday

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare read-only/idempotent/non-destructive; the description carries far more. It discloses that aggregation is computed server-side, that sum is refused on state measurements, the semantics of period_in_progress/clipped_by_window, partial:true exclusion from aggregates, excluded_days with reasons, missing days never zeroed, and the partner-sharing allowlist (sleep, water, weight only).

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?

Long (~450 words), but front-loaded and organized into labeled blocks (series, days, aggregation/granularity, honesty guarantees, output shape, partner, siblings), and nearly every sentence carries operational content. It is on the verbose edge for a tool description, yet the density justifies most of the length.

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?

Despite an output schema existing, the description goes further by explaining the columns/rows table shape, the coverage block, and the more_available warning that prevents misreporting the extent of a user's history. For an 8-parameter, multi-mode tool with 0% schema coverage, nothing an agent needs to call it correctly 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?

Schema description coverage is 0%, so the description must compensate, and it does for series, days, since, until, aggregation, granularity and partner — including value formats ('YYYY-MM-DD'), defaults, enum-like options (avg/sum/min/max/latest/none; day/week/month/weekday) and interaction rules (days ignored when since/until given). The one gap is `fresh`, which is never explained anywhere, so coverage is strong but not complete.

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 verb+resource: 'Read any one-number-per-day health series: steps, resting HR, HRV, weight', and immediately draws the boundary against siblings (list_metric_series, get_sleep_nights, get_intraday, get_metric_trend, compare_metric_periods, correlate_metric_series). An agent can route to this tool without opening any other 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?

Explicit when-to-use rules: call list_metric_series first rather than guessing names; use days for 'newest N days that have data' versus since/until for a calendar window; pass the five activity series as a list to get a whole day in one call. It also states exclusions and redirects richer kinds (workouts, food, symptoms, cycle, profile, intraday, trends) to their own tools.

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

get_metric_trendMetric trendA
Read-onlyIdempotent

Least-squares trend for one daily series: slope per day, endpoints, spread.

series is a name from list_metric_series. days selects the newest N days THAT HAVE DATA, not the last N calendar days — compare n_days with span_days to see whether the history is dense or sparse. since / until ("YYYY-MM-DD") fit a calendar window instead.

Returns arithmetic only. slope_per_day is in the series' own unit per day and carries no threshold, band or verdict; fewer than 3 days returns a null slope with a reason rather than a number fitted to noise.

Carries a coverage block over the days the arithmetic used: quote coverage.days_covered and coverage.span_days beside any number here. coverage.window_satisfied: false alone does NOT mean a short history — it is also false when older days exist beyond the window; coverage.more_available tells the two apart.

ParametersJSON Schema
NameRequiredDescriptionDefault
daysNo
freshNo
sinceNo
untilNo
seriesYes
partnerNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already carry the safe-read profile (readOnly, idempotent, non-destructive, closed-world), so the description's marginal value is its edge-case disclosure: arithmetic only, no threshold/band/verdict, and a null slope with a reason when fewer than 3 days exist. The coverage-block interpretation is useful but partially overlaps the output schema.

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

Conciseness4/5

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

Front-loaded with the core purpose, then parameter and output semantics in compact sentences. Dense but each sentence carries meaning; slightly long for a trend getter but no filler.

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 trend tool with an output schema, the description supplies the edge-case and interpretation context an agent needs (density caveat, null-slope rule, coverage pairing). The gap is the two undocumented parameters, fresh and partner.

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

Parameters3/5

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

Schema description coverage is 0%, so the description must compensate. It meaningfully explains series, days ('newest N days THAT HAVE DATA, not the last N calendar days') and since/until format, but leaves fresh and partner completely undocumented in both schema and description.

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 computation and resource: 'least-squares trend for one daily series: slope per day, endpoints, spread.' This clearly distinguishes it from a raw-value sibling like get_metric. It stops short of naming an alternative (compare_metric_periods, correlate_metric_series) to route against.

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 gives condition semantics (series comes from list_metric_series; days vs since/until select different windows), which implies when each parameter applies. But there is no explicit when-to-use-this-vs-another-tool guidance, and no exclusions, so it remains implied rather than directive.

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

get_notesNotesA
Read-onlyIdempotent

Decrypt recent free-text notes (day annotations) locally.

SENSITIVE free text. Each note carries owner_user_id (who wrote it), target_kind, and target_date (the local day it annotates) — join against the same-day metric data for pattern analysis. Kinds: "sleep" | "menstrual" are written manually in the iOS app by either partner (e.g. "昨晚舍友很吵" on a sleep day); "mood" | "general" are agent-authored via log_note. Pass target_kind to filter. Stays on-device, never re-exported.

Returns notes about YOU by default. partner=true returns notes about your partner: the ones they wrote themselves and shared, plus the ones your AI recorded about them (about: "partner", written with log_note_append(partner=true)).

Carries a coverage block: quote coverage.days_covered (distinct days, not the row count) and coverage.span_days beside any average or trend. coverage.window_satisfied: false alone does NOT mean a short history: it is also false when older days exist beyond your window. 🔴 Before saying how far back someone's data goes, read coverage.more_available: true means this server can decrypt days OLDER than first_day that your limit left behind — re-read with a larger limit, or quote coverage.oldest_available as the real start of their history. Never report a limit-shaped window as the extent of their data.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo
partnerNo
target_kindNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), so the bar is lower. The description adds substantial context beyond them: data stays on-device and is never re-exported, the text is SENSITIVE, and it gives detailed coverage-block semantics (days_covered, span_days, window_satisfied, more_available) that would otherwise mislead. It does not explain the 'fresh' parameter behavior, so not a full 5.

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 purpose and filter behavior are front-loaded, and each block earns its place. It is somewhat long due to the detailed coverage guidance and the red-flag warning, but that content is task-critical rather than padding.

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

Completeness4/5

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

An output schema exists, so return values need not be restated; the description focuses on semantics and interpretation instead. It is thorough for a read tool but leaves the 'fresh' parameter and the exact enum formatting ('sleep' | 'menstrual' | 'mood' | 'general' vs the untyped schema) unaddressed.

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% across 4 parameters. The description compensates for partner (explains the about: 'partner' distinction) and target_kind (lists the valid kinds and the filter role), and touches limit indirectly via the larger-limit advice. However, 'fresh' is never explained, so the description only partially covers the undocumented parameters.

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 with scope: 'Decrypt recent free-text notes (day annotations) locally.' It also names the kind categories and the sibling write path (log_note/log_note_append), so an agent can distinguish this read tool from the logging tools without opening schemas.

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 clear usage context: default returns notes about YOU, partner=true returns notes about the partner, and 'Pass target_kind to filter.' It implies but does not explicitly state when NOT to use this versus alternatives like get_metric or correlate_metric_series, so it falls short of the top tier.

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

get_sleep_detailSleep stage detailA
Read-onlyIdempotent

Per-night sleep stages with per-stage HR/RR — depth on a few nights.

Which sleep tool to use: · THIS one for going deep on one or two specific nights (stage bands, per-stage vitals). It defaults to 2 nights because each is ~3k characters; see the SIZE note below. · get_sleep_nights for anything spanning time — "how did I sleep this week/month/year", patterns, worst nights. One compact row per night, a whole year in one read. Reach for it whenever the question is about a period rather than a night, and do not conclude from THIS tool's two rows that only two nights exist. · get_metric with the sleep series (bedtime_minutes, deep_sleep_minutes, awakenings, ...) for averages computed for you.

(This used to call itself "the primary tool for detailed sleep analysis", which read as "use this one for sleep" and handed back two days to anyone who asked how their week went.)

Returns stage_intervals (contiguous stage bands with start/end), stage_minutes, and stage_vitals (per-stage HR/RR min/mean/max). Reads YOUR nights by default; partner=true reads your partner's shared sleep.

limit = the newest N nights, 2 by default. To read a particular night, pass its date as since and until ("YYYY-MM-DD", the local day the night ENDS on — the local_date / date column of get_sleep_nights); a wider since / until reads every night in that window, and limit then applies only if you pass it.

⚠️ SIZE: each night is ~3k characters as returned (a week ~21k). Setting include_timeline=True adds the raw per-sample array (hr, rr, stage, time) — about 13k characters PER NIGHT, which will overflow a typical 25k-token client budget after 2-3 nights. Ask for it only when you need sample-level vitals (e.g. "when exactly did HR spike"); stage_vitals already answers per-stage questions. Raise limit for trends, but keep it low whenever include_timeline=True.

⚠️ is_in_bed_only: true means sleep was NEVER MEASURED that night (the Watch wasn't worn) — NOT that the person slept zero. duration_label reads "no sleep data" and in_bed_minutes holds the time actually recorded in bed. Report it as "no sleep data (in bed ~Xh)", never as "slept 0 hours".

Carries a coverage block: quote coverage.days_covered (distinct days, not the row count) and coverage.span_days beside any average or trend. coverage.window_satisfied: false alone does NOT mean a short history: it is also false when older days exist beyond your window. 🔴 Before saying how far back someone's data goes, read coverage.more_available: true means this server can decrypt days OLDER than first_day that your limit left behind — re-read with a larger limit, or quote coverage.oldest_available as the real start of their history. Never report a limit-shaped window as the extent of their data.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo
sinceNo
untilNo
partnerNo
include_timelineNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior3/5

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

Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description goes well beyond that with size/budget warnings (~3k chars per night, 13k with include_timeline), the `is_in_bed_only` non-measurement trap, and coverage-block reporting rules — genuinely useful behavioral context. However, the volume of caveats is disproportionate to the tool's actual behavior, and many are output-interpretation guidance rather than invocation behavior, so it earns solid but not top marks.

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 purpose, then clearly delimited routing/returns/SIZE/semantics sections. The parenthetical aside about the tool's former self-description is editorial waste, but most of the length is load-bearing given the size-budget traps and the coverage-reporting contract.

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 6-parameter, 0%-coverage, read-only analytics tool, the description covers invocation parameters, the output shape (stage_intervals/stage_minutes/stage_vitals) despite an output schema existing, size budgets, data-absence semantics, and the coverage block's correct interpretation. Nothing an agent needs to call or interpret it correctly is missing.

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?

Schema description coverage is 0%, so the description carries the full load and does so for limit (newest N, default 2), since/until (exact 'YYYY-MM-DD' format, local day the night ENDS on, and how limit interacts with a window), include_timeline, and partner. Only `fresh` is left unexplained, which is a minor gap against five well-documented parameters.

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 verb+resource ('Per-night sleep stages with per-stage HR/RR') and immediately scopes it ('depth on a few nights'). It explicitly names and distinguishes both alternatives, `get_sleep_nights` and `get_metric`, so an agent can route correctly without opening any 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?

Contains a dedicated routing block stating when to use this tool (one or two specific nights), when NOT to (anything spanning time, periods), and which sibling to use instead for each case. It even warns against a specific misinference ('do not conclude from THIS tool's two rows that only two nights exist').

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

get_sleep_nightsSleep, every nightA
Read-onlyIdempotent

Every night as one compact row — the tool for reading a sleep HISTORY.

One row per night, newest first: date, weekday, bed and wake time, time asleep, deep / REM / core / awake minutes, deep and REM share, number of awakenings, longest unbroken stretch of sleep, heart and breathing rate while asleep, and any OTHER sleep that day (naps, the second half of a broken night) — so a day's total sleep is asleep plus other. Column names come once in columns; each row is the values in that order, so a whole year is ~40k characters. Read it when the question is about a period or a pattern: weekends vs weekdays, drifting bedtimes, which nights were worst, what changed after a date.

Which sleep tool: · THIS one for any span of time. · get_metric with the sleep series (sleep_minutes, bedtime_minutes, deep_sleep_minutes, awakenings, ...) when you want the arithmetic — weekly/monthly averages computed for you, from these same rows. · get_sleep_detail for one or two nights in depth (stage bands, per-stage vitals).

limit = newest N nights (default 120; pass 400 for everything, and check coverage.more_available). since = "YYYY-MM-DD" keeps every night on or after that day — use it for "last month" rather than guessing a limit; with since, limit applies only if you pass it.

Read legend once. Five flags, comma-separated when a night has several: unworn means sleep was never measured that night (not zero sleep), motion_inferred means phone motion guessed it (leave it out of averages, as get_metric does), daytime means the day's only sleep was a daytime nap (real, but not a night — out of get_metric's nightly series, still a row here), short means a main sleep under 3 h with no stages — a nap or a partly recorded night, left out of get_metric's sleep-duration and clock series — and no_stages means the total is known but the stage columns are null. A null anywhere is "not measured", never zero. Reads YOUR nights; partner=true reads your partner's shared sleep.

Carries a coverage block: quote coverage.days_covered beside any average, and read coverage.more_available before saying how far back the history goes.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo
sinceNo
partnerNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already establish read-only, idempotent, non-destructive behavior, and the description goes well beyond: it discloses approximate output size (~40k chars/year), that column names come once in `columns`, the `legend` flag semantics (`unworn` means never measured, not zero; `motion_inferred` should be excluded from averages), that null means 'not measured, never zero', and that `partner=true` reads a partner's shared data. This is exactly the operational context annotations cannot carry.

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?

It is a long description, but the load-bearing facts are front-loaded (one row per night, newest first, column layout) and the tool-selection routing and legend semantics are each stated once. The length is justified by the tool's complexity, though the schema/legend material is dense enough that it could be tightened.

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

Completeness4/5

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

An output schema exists, yet the description still usefully explains the row/column shape and the `coverage` block (`days_covered` to quote beside averages, `more_available` before claiming history depth), which is high-value context for correct answer framing. Combined with the flag semantics and null rules, coverage is strong; the sole miss is the unexplained `fresh` parameter.

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 0%, so the description must carry the entire burden. It explains `limit` (newest N nights, default 120, 400 for everything), `since` (YYYY-MM-DD, inclusive, overrides guessing a limit), and `partner` (reads partner's shared sleep). However `fresh` is never mentioned anywhere in the description or schema, leaving one of four parameters completely undocumented.

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 the resource ('one compact row per night'), the ordering ('newest first'), and the enumeration of every returned column, giving an agent a complete picture of what the tool produces. The explicit 'Which sleep tool' section names the two sibling alternatives (get_metric, get_sleep_detail) and the condition that selects each, so the tool is separable from its siblings without opening any 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?

Gives explicit when-to-use ('any span of time', 'period or a pattern: weekends vs weekdays, drifting bedtimes'), when-to-use-elsewhere (get_metric for precomputed arithmetic, get_sleep_detail for one or two nights), and parameter-level guidance for choosing between `since` and `limit`. Nothing about tool selection is left to inference.

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

get_strength_logStrength logA
Read-onlyIdempotent

Decrypt recent strength-training sessions locally (newest first).

Exercise-level detail HealthKit's workout type cannot carry: each session lists exercises with their sets (weightKg × reps), an optional session note, and total_volume_kg (Σ weight × reps). Logged manually in Vaultbeat; owner's own sessions only — strength has no partner fan-out. Join date against sleep/HRV/weight for training-load analysis. Pass limit_days to cap how many sessions return. duplicate_sessions, when present, lists stored entries that only repeat another session of the same day; they are already left out of sessions and session_count, so do not add their volume back.

Carries a coverage block: quote coverage.days_covered (distinct days, not the row count) and coverage.span_days beside any average or trend. coverage.window_satisfied: false alone does NOT mean a short history: it is also false when older days exist beyond your window. 🔴 Before saying how far back someone's data goes, read coverage.more_available: true means this server can decrypt days OLDER than first_day that your limit left behind — re-read with a larger limit, or quote coverage.oldest_available as the real start of their history. Never report a limit-shaped window as the extent of their data.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo
limit_daysNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

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

Annotations already declare readOnly/idempotent/non-destructive, and the description adds substantial context beyond them: local decryption, owner's-sessions-only scope (no partner fan-out), duplicate_sessions already excluded from counts, and detailed `coverage`/`more_available` semantics with a warning not to misreport a limit-shaped window as full history. This is rich behavioral disclosure.

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-loads the core purpose and scopes return fields before the operational caveats. It is long and mostly earns its length given the coverage-safety warnings, though the `coverage`/`more_available` instruction block is dense enough to border on over-explanation.

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?

Despite an output schema existing, the description usefully explains the meaning of key return fields (exercise sets, total_volume_kg, duplicate_sessions, coverage block), which the schema alone would not convey. For a 3-param read tool it is complete enough to call correctly and interpret results safely.

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

Parameters3/5

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

Schema description coverage is 0%, so the description must compensate. It explains `limit_days` ('cap how many sessions return') and references `limit` indirectly ('re-read with a larger `limit`'), but never explains the `fresh` parameter at all, leaving one of three params undocumented in both schema and description.

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 ('Decrypt recent strength-training sessions locally') with ordering (newest first) and explicitly distinguishes itself from the sibling `get_workouts` by noting it carries exercise-level detail HealthKit's workout type cannot. An agent can pick this over `get_workouts` without opening a schema.

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

Usage Guidelines4/5

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

Gives clear usage context: join `date` against sleep/HRV/weight for training-load analysis, and pass `limit_days` to cap results. It implicitly contrasts with `get_workouts` via the HealthKit remark, but never explicitly states when NOT to use it or names an alternative to prefer for non-strength data.

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

get_symptomsSymptomsA
Read-onlyIdempotent

Decrypt recent symptoms locally, grouped by data owner — both sources.

SENSITIVE. Each entry in owners carries owner_user_id and two lists: · days — symptoms imported from Apple Health (cramps, headache, fatigue, coughing…), one row per day with each sample's severity (mild/moderate/severe/present/…). These reach this server only when the user opted in on iOS — their own AI toggle, or the partner-AI toggle for a partner's. symptom_counts counts logged DAYS per type. · reported — episodes a person reported themselves, through log_symptom or the Vaultbeat app: entry_id, symptom_type, display_name (their own words), severity, local_date, onset_at / end_at (end_recorded: false means no end was logged — ask, do not assume it is still going), duration_minutes, body_location, triggers, note. reported_counts counts EPISODES per type. A type that exists in Apple Health is spelled the same in both lists (healthkit_type: true), so "headache" from either source is one symptom. To correct or remove a reported entry use update_symptom / delete_symptom with its entry_id; imported days are edited in Apple Health. Stays on-device, never re-exported.

Returns YOUR symptoms by default; partner=true returns your partner's Apple Health days only (if they share symptoms with you from their own app). A partner's reported episodes never reach you: the app and log_symptom seal them for their owner's own AI alone, so an empty reported under partner=true means "not shareable", not "none logged".

Carries a coverage block: quote coverage.days_covered (distinct days, not the row count) and coverage.span_days beside any average or trend. coverage.window_satisfied: false alone does NOT mean a short history: it is also false when older days exist beyond your window. 🔴 Before saying how far back someone's data goes, read coverage.more_available: true means this server can decrypt days OLDER than first_day that your limit left behind — re-read with a larger limit, or quote coverage.oldest_available as the real start of their history. Never report a limit-shaped window as the extent of their data.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo
partnerNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

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

Annotations already declare readOnly, idempotent, non-destructive behavior, but the description adds substantial context: sensitive on-device data, Apple Health opt-in requirements, partner reported episodes never reaching the caller, empty partner reported meaning 'not shareable', and coverage.more_available pitfalls. This far exceeds the safety profile provided by annotations.

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

Conciseness4/5

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

Front-loads the core purpose and uses structured bullets for complex source and coverage differences. However, it is very long and enumerates many return fields despite an output schema already existing, so it is not maximally concise.

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 complex, sensitive tool with an output schema, it covers source distinctions, partner limitations, correction workflows, and coverage caveats well. The main omission is the fresh parameter, and some return-field explanation duplicates the output schema.

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

Parameters3/5

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

Schema description coverage is 0%, so the description must compensate. It clarifies partner=true and references using a larger limit in the coverage warning, but it never explains the fresh parameter, leaving one of three parameters undocumented.

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 ('Decrypt') and resource ('symptoms'), with scope 'grouped by data owner — both sources' and default/partner behavior. It also distinguishes correction paths by naming update_symptom/delete_symptom, so an agent can tell what this tool is for.

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 that it returns YOUR symptoms by default, that partner=true returns only a partner's Apple Health days, and that reported entries are corrected via update_symptom/delete_symptom. It lacks an explicit comparison to unrelated siblings like get_metric/get_notes, but gives clear usage context and alternatives for mutation.

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

get_user_profileHealth profileA
Read-onlyIdempotent

Read YOUR health profile: biological sex, age, date of birth, height.

Use it before anything that depends on sex, age or height — basal metabolic rate, heart-rate zones, VO2 max bands, BMI. It is never shared between partners, so this tool has no partner option.

profile.sex_source says where the sex came from: "chosen" (the person picked it in the app) or "apple_health" (a field in Apple Health they may never have looked at). Every field may be null; profile itself is null when nothing was uploaded, and the reply's note explains why that cannot be narrowed down. Never fill a null in with a guess.

A profile is not a series: its coverage.days_covered is 0 by construction, and the reply carries no upload time — the stored row is replaced in place on every edit, so nothing here says how current the values are.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

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

Annotations already cover readOnly/idempotent/non-destructive/closed-world, yet the description adds substantial behavior the structured fields cannot: every field may be null, `profile` is null when nothing was uploaded, the reply's `note` explains why, nulls must never be guessed, and the row is replaced in place so there is no upload timestamp indicating currency. It even clarifies that a profile is not a series and `coverage.days_covered` is 0 by construction.

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 purpose and the pre-requisite use case are front-loaded in the first two paragraphs, and each subsequent sentence carries a distinct constraint (null handling, no partner, non-series semantics, no upload time). No filler or restatement of the name.

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 an output schema present and no nested objects, the description need not describe return shapes, yet it still pre-empts the two most likely agent errors: treating a profile like a time series and hallucinating values for null fields. Nothing needed to call it correctly is missing.

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

Parameters2/5

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

Schema description coverage is 0% and the sole parameter `fresh` is never mentioned, let alone explained (is it a cache bypass? a re-read?). The description compensates richly for output semantics but leaves the one input entirely undocumented, which is the reverse of what a low-coverage schema requires.

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 precise verb and resource ("Read YOUR health profile") and enumerates the exact fields returned: biological sex, age, date of birth, height. It also differentiates from siblings that take a partner option by asserting this tool has none, so the agent can place it relative to get_metric/get_symptoms without opening a schema.

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

Usage Guidelines4/5

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

Gives an explicit use condition: call it before anything depending on sex, age or height (BMR, HR zones, VO2 max bands, BMI). It also states a constraint (no partner option), which functions as a when-not. It stops short of naming a sibling alternative or a case where a different profile-ish tool should be preferred.

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

get_workoutsWorkoutsA
Read-onlyIdempotent

Decrypt recent workout sessions (type, duration, calories, distance). Reads YOUR workouts; workouts are never shared between partners, so this tool has no partner option.

Carries a coverage block: quote coverage.days_covered (distinct days, not the row count) and coverage.span_days beside any average or trend. coverage.window_satisfied: false alone does NOT mean a short history: it is also false when older days exist beyond your window. 🔴 Before saying how far back someone's data goes, read coverage.more_available: true means this server can decrypt days OLDER than first_day that your limit left behind — re-read with a larger limit, or quote coverage.oldest_available as the real start of their history. Never report a limit-shaped window as the extent of their data.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
limitNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

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

Annotations already establish a safe read-only, idempotent profile, and the description layers on substantial non-obvious behavior: server-side decryption, the fact that the returned window is capped by `limit`, and the critical caveat that coverage.window_satisfied=false and coverage.more_available=true can mask older available history. This is exactly the kind of data-completeness behavior an agent cannot infer from structured fields.

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

Conciseness4/5

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

Front-loaded with purpose in the first sentence, then progressively narrower interpretation rules. The coverage guidance is dense and slightly repetitive ('Never report a limit-shaped window as the extent of their data' restates the more_available warning), but every block still carries actionable 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?

An output schema exists, so return values need no explanation, and the description nonetheless documents the coverage block's interpretation rules thoroughly. What's missing is the meaning of the `fresh` parameter and any routing versus the strength/metric siblings, which keeps it short of fully 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 carry the load. It functionally explains `limit` (it truncates older days, and a larger value reveals more history) but never mentions `fresh` at all, leaving one of two parameters entirely undocumented anywhere.

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 ('Decrypt recent workout sessions') and enumerates the payload fields (type, duration, calories, distance), so the agent knows exactly what comes back. It also delimits scope by declaring workouts are never partner-shared, which separates it from the partner-aware siblings in this server.

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 clear context ('Reads YOUR workouts', no partner option) and detailed instructions on how to consume the result safely. It does not, however, say when to prefer this over adjacent siblings such as get_strength_log or get_metric, which is a real gap given the overlapping fitness-domain tools.

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

list_metric_seriesList analysable seriesA
Read-onlyIdempotent

Every series, with how much data actually backs each one.

Call this BEFORE guessing a series name, and before concluding a kind is empty. Each row carries rows / first_date / last_date / latest, so "does this person track VO2max at all" is answered here in one call instead of by reading the kind and getting nothing back.

rows = the days that HAVE a value for that series — counted the way get_metric reads it, so rows: 2 means get_metric can return at most 2 points. A kind's series can differ: a partner with 14 weigh-ins may have a BMI on only 2 of those days.

cumulative: true marks the series whose daily value ACCRUES over the day (steps, energy, water). On those the newest value is routinely a partial day and must not be compared against completed ones — that is what makes "steps are down today" wrong at 9am. Measurements of a state (resting HR, VO2max) carry false and need no such care.

Sleep appears here as numbers (duration, timing, stages, awakenings, vitals); read the nights themselves with get_sleep_nights. Kinds with a richer shape (workouts, strength sets, food, notes, symptoms, cycle) are deliberately absent — flattening them to one number per day would answer a question you did not ask; read them with their own tool.

ParametersJSON Schema
NameRequiredDescriptionDefault
freshNo
partnerNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

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

Annotations already declare the safe read-only idempotent profile, yet the description still adds real behavioral context beyond them: what `rows` counts, that `cumulative: true` series have routinely-partial newest values that must not be compared to completed ones, and that state measurements carry `false`. That partial-day caveat is exactly the kind of trap an agent would otherwise fall into.

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 key instruction, then dense supporting detail where every paragraph (rows semantics, cumulative warning, sleep note, deliberate absences) earns its place. It runs long, but no sentence is filler.

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

Completeness4/5

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

An output schema exists so return values needn't be described, and the description still adds the useful semantics of the returned fields. The one real hole is parameter meaning — an agent can call it with defaults, but cannot knowingly use `fresh` or `partner` from this text.

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

Parameters2/5

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

Schema description coverage is 0% and the description never explains the `fresh` parameter at all, nor defines what `partner` filters. `partner` is only obliquely implied by the example about 'a partner with 14 weigh-ins.' With two undocumented booleans, the description does not compensate for the schema gap.

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 verb+resource and scope: 'Every series, with how much data actually backs each one.' It explicitly distinguishes itself from get_metric (rows counted the way get_metric reads it), get_sleep_nights, and the richer-shape tools that are deliberately absent, so an agent can place it against siblings without opening any 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?

Gives an explicit when-to-use imperative ('Call this BEFORE guessing a `series` name, and before concluding a kind is empty') and names the concrete alternative tools for the cases it excludes (get_sleep_nights for nights, 'their own tool' for workouts/strength/food/notes/symptoms/cycle). The routing is unambiguous.

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

log_food_appendAdd to food logA

Add meals to a day WITHOUT touching what is already logged (agent write).

This tool cannot delete anything you did not send. Your meals are appended to whatever the day already holds; a meal whose name matches an existing meal gets its items appended to that meal. This is the right tool for "log the snack I forgot" / "add dinner to today" — which is almost every follow-up write of the day.

Reach for log_food_entry ONLY when you intend the supplied meals to become the day's ENTIRE log and everything else to be deleted.

date is the LOCAL calendar day, "YYYY-MM-DD". meals is a list of {name?, timeOfDay?, items: [...], note?} where each item is {food, portion?, note?, kcal?, proteinGrams?, fatGrams?, carbGrams?}, e.g. [{"name": "lunch", "items": [{"food": "香蕉", "portion": "1 根", "kcal": 105}]}]. Everything but food is optional so a rushed "just log 香蕉" still works; when you DO estimate nutrition at logging time, put the numbers in the structured fields (snake_case aliases like protein_g are accepted) — they persist for later sessions instead of being re-guessed each read.

ESTIMATING FROM A PHOTO: look for something of known size in the frame first — a utensil, a hand, a coin, the rim of a standard plate — and calibrate the portion against it. With no such reference an image cannot settle portion size, and portion size is what the whole estimate rests on. In that case say so in your reply and give a range rather than a precise-looking number. These values are persisted and summed into daily totals later, so a confident "650 kcal" that is wrong does more damage than "roughly 500-700, nothing in frame to judge size by" — the first silently poisons a week of trends, the second invites a correction. [keep the photo-estimation paragraph above in sync with log_food_entry's copy]

This tool deliberately cannot set the day's note: that field is replace-only, and a tool that promises to delete nothing must not carry an exception. Use log_food_entry to change it.

The result is the same shape log_food_entry returns. replaced_meals is always [] here — that empty list is the receipt that this call deleted nothing. Encrypted end-to-end before it ever leaves this machine.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateYes
mealsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Far exceeds what annotations declare. It explains the merge behavior by meal name, that replaced_meals is always [] as a deletion receipt, that no note can be set, and the E2E encryption. Annotation destructiveHint=false is corroborated, not contradicted.

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-loads the core append semantics and the log_food_entry contrast, with clear paragraph separation. The photo-estimation paragraph is somewhat long and contains an editorial comment ('keep the photo-estimation paragraph above in sync') that is meta-instruction rather than tool guidance, slightly diluting focus.

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 mutation tool with no param docs in the schema, this covers interaction semantics, parameter shapes, return value shape, failure modes, and side effects. Nothing an agent needs to invoke it correctly is missing.

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

Parameters5/5

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

Schema coverage is 0%, so the description must compensate—and it does thoroughly: date format 'YYYY-MM-DD', full meals object shape with every field, a concrete example, optional-field rules, accepted snake_case aliases, and photo-estimation heuristics. It adds far more meaning than the bare 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+resource (append meals to a day) and explicitly distinguishes from its sibling: 'Reach for log_food_entry ONLY when you intend the supplied meals to become the day's ENTIRE log.' The append-vs-replace semantics are unambiguous.

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?

Explicit when-to-use ('log the snack I forgot', 'add dinner to today', almost every follow-up write) and when-not-to-use (log_food_entry for full replacement). It even closes loopholes by explaining why note-setting is excluded. Nothing is left to inference.

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

log_food_entryLog food (replaces day)A
Destructive

Log one day's food intake on the owner's behalf (agent write).

⚠️ THIS TOOL DELETES. The supplied meals become the day's ENTIRE log — any meal you don't re-send is silently deleted. If you meant to ADD to a day rather than replace it, STOP and call log_food_append instead; it cannot delete anything.

(merge=True still does the same thing as log_food_append and keeps working for callers that already use it. New callers should use the separate tool: which one you called is visible to the owner, a flag buried in the arguments is not.)

The result carries replaced_meals — the meals this call deleted. If that list is non-empty and you did not intend to replace the day, you just destroyed them; re-send them with merge=True.

note=None LEAVES THE EXISTING NOTE ALONE in both modes (pass note="" to clear it).

date is the LOCAL calendar day, "YYYY-MM-DD". meals is a list of {name?, timeOfDay?, items: [...], note?} where each item is {food, portion?, note?, kcal?, proteinGrams?, fatGrams?, carbGrams?}, e.g. [{"name": "lunch", "items": [{"food": "香蕉", "portion": "1 根", "kcal": 105}]}]. Everything but food is optional so a rushed "just log 香蕉" still works; when you DO estimate nutrition at logging time, put the numbers in the structured fields (snake_case aliases like protein_g are accepted) — they persist for later sessions instead of being re-guessed each read.

ESTIMATING FROM A PHOTO: look for something of known size in the frame first — a utensil, a hand, a coin, the rim of a standard plate — and calibrate the portion against it. With no such reference an image cannot settle portion size, and portion size is what the whole estimate rests on. In that case say so in your reply and give a range rather than a precise-looking number. These values are persisted and summed into daily totals later, so a confident "650 kcal" that is wrong does more damage than "roughly 500-700, nothing in frame to judge size by" — the first silently poisons a week of trends, the second invites a correction. [keep the photo-estimation paragraph above in sync with log_food_append's copy] Encrypted end-to-end before it ever leaves this machine.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateYes
noteNo
mealsYes
mergeNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

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

Annotations declare destructiveHint=true, and the description goes well beyond that by naming exactly what is destroyed (any meal not re-sent), how the caller can detect it (replaced_meals in the result), the note=None vs note="" semantics, the deprecation status of merge, and that data is encrypted end-to-end. This is exactly the kind of context annotations cannot carry.

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?

It is long, but the length is justified: the destructive warning is front-loaded in caps before any detail, and each subsequent paragraph covers a distinct failure mode (accidental deletion, note clearing, date locality, photo estimation). The only waste is the leaked editorial note about keeping the photo paragraph in sync with log_food_append, which is boilerplate for maintainers rather than agents.

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 destructive, 4-parameter write tool with an output schema, the description covers the risks an agent needs to call it safely: replacement semantics, recovery via replaced_meals, note handling, and estimation guidance. Return-value shape is left to the output schema, which is the right division of labor.

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?

Schema description coverage is 0%, so the description must carry the full burden and does: date format and timezone semantics, the nested meals shape with a concrete example, which fields are optional, accepted snake_case aliases, and the non-obvious note=None behavior. merge is also explained as a legacy alias.

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 specific verb and resource ('Log one day's food intake on the owner's behalf') plus the critical scope fact that it replaces the whole day rather than adding to it. It explicitly names the sibling it is not (log_food_append), so an agent can route correctly 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 Guidelines5/5

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

It gives an explicit when-not rule with a stop condition ('If you meant to ADD to a day rather than replace it, STOP and call log_food_append instead; it cannot delete anything'), and explains the legacy merge=True path and why new callers should prefer the separate tool. Nothing about tool selection is left to inference.

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

log_noteLog note (replaces note)A
Destructive

Log a free-text note on the owner's behalf (agent write).

For narratives that belong next to the metric data instead of in chat history: kind="mood" for emotional state ("为什么今天情绪低落"), kind="general" for day events worth joining against sleep/HRV later. (sleep/menstrual notes stay iOS-authored — this tool refuses them.)

⚠️ THIS TOOL DELETES. There is one note per (kind, day), and your text becomes its ENTIRE contents — anything already written for that kind+day is silently deleted. If you meant to ADD to a day rather than replace it, STOP and call log_note_append instead; it cannot delete anything.

(merge=True still does the same thing as log_note_append and keeps working for callers that already use it. New callers should use the separate tool: which one you called is visible to the owner, a flag buried in the arguments is not.)

The result carries replaced_text — the note this call deleted. If it is non-null and you did not intend to replace, you just destroyed that text; re-send it with merge=True.

Symptoms do NOT go here: use log_symptom, one call per symptom. A note is free text nothing can be matched against; a symptom entry keeps its type, severity and timing so it can later be lined up with sleep, food and heart rate. A note is for what surrounds it (mood, events, how a day went).

date = LOCAL calendar day "YYYY-MM-DD" (default today). Read back via get_notes (optionally target_kind="mood"/"general"). Encrypted end-to-end before it ever leaves this machine.

partner=true records a note ABOUT your partner ("she had a stomach ache this morning"). It is stored in YOUR account and sealed to you and this machine only — your partner's account is never touched, and it never mixes into your own notes: plain reads leave it out, get_notes(partner=true) returns it. Default false = a note about you.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNo
kindNogeneral
textYes
mergeNo
partnerNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Annotations declare destructiveHint=true, and the description goes well beyond that: it explains the one-note-per-(kind,day) model, that text becomes the ENTIRE contents, that existing text is silently deleted, the replaced_text recovery path, E2E encryption, and partner-account scoping. This is exactly the extra context a destructive tool needs.

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 purpose and the destructive warning, but the merge=True legacy explanation and the repeated deletion caveat make it longer than strictly necessary; still, each block targets a distinct failure mode so little is waste.

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?

Covers read-back via get_notes, the replaced_text output field (so no separate return-value explanation needed), encryption, and partner isolation — complete for a destructive 5-param tool with an output schema.

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?

Schema coverage is 0%, so the description carries full burden and does: date format ('YYYY-MM-DD', default today), kind values, text semantics (replaces, not appends), merge as a legacy alias, and partner=true meaning a note about the partner stored in the caller's own account.

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 ('Log a free-text note on the owner's behalf') and immediately distinguishes itself from siblings by naming log_note_append and log_symptom as the wrong destinations for certain content.

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 routes: narratives vs chat history, kind='mood' vs 'general', sleep/menstrual notes refused, add-intent should go to log_note_append, symptoms to log_symptom. When-to-use and when-not-to-use are both covered with concrete examples.

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

log_note_appendAdd to noteA

Add a line to a day's note WITHOUT erasing what is already there (agent write).

There is one note per (kind, day). This tool appends your text to it on a new line and cannot delete what is already written.

The second note of a day is the normal case, not the exception — a morning event and an evening one — and log_note would replace the first with the second. Symptoms do NOT go here: use log_symptom, which keeps type, severity and timing as fields a later analysis can match.

Reach for log_note ONLY when you intend your text to become the note's ENTIRE contents and whatever is there now to be deleted (e.g. correcting something you yourself wrote a minute ago).

kind="general" for day events worth joining against sleep/HRV later, kind="mood" for emotional state. (sleep/menstrual notes stay iOS-authored — this tool refuses them.) date = LOCAL calendar day "YYYY-MM-DD" (default today). Read back via get_notes.

The result is the same shape log_note returns. replaced_text is always null here — that null is the receipt that this call deleted nothing. Encrypted end-to-end before it ever leaves this machine.

partner=true records a note ABOUT your partner ("she had a stomach ache this morning"). It is stored in YOUR account and sealed to you and this machine only — your partner's account is never touched, and it never mixes into your own notes: plain reads leave it out, get_notes(partner=true) returns it. Default false = a note about you.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNo
kindNogeneral
textYes
partnerNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only say readOnlyHint=false and destructiveHint=false. The description adds substantial behavioral context: it cannot delete existing content, `replaced_text` is always null as a 'receipt', it refuses sleep/menstrual notes, it is end-to-end encrypted before leaving the machine, and `partner=true` isolation semantics. This goes well beyond the annotations.

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

Conciseness4/5

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

It is long but front-loads the core append semantics, then layers distinctions and parameter notes. Every paragraph earns its place, though a few parentheticals could be tightened. The structure is logical and readable.

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 4 parameters, 0% schema description coverage, a mutation with no destructive annotation, and an output schema, the description covers everything an agent needs: what it does, what it refuses, how to read back via `get_notes`, parameter meanings, and return shape (same as `log_note`, `replaced_text` always null).

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?

Schema coverage is 0%, so the description must carry parameter meaning, and it does: it explains `kind` values 'general' vs 'mood' and that sleep/menstrual are refused, `date` format is LOCAL 'YYYY-MM-DD' defaulting to today, `partner=true` records a note about the partner stored in your account and excluded from plain reads, and `text` is the appended line.

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 specific verb+resource with scope: 'Add a line to a day's note WITHOUT erasing what is already there.' It immediately distinguishes itself from the sibling `log_note` by contrasting append vs. replace behavior, so an agent can select correctly without inspecting schemas.

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

Usage Guidelines5/5

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

It gives explicit when-to-use (append to an existing note, the normal case of a second note of the day), when-not-to-use (`log_note` only when replacing the entire contents), and points to an alternative for symptoms (`log_symptom`). This is exactly the routing guidance needed among several sibling write tools.

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

log_strength_appendAdd to strength logA

Add exercises to a session WITHOUT touching what is already logged (agent write).

This tool cannot delete anything you did not send. Your exercises are appended to the day's existing session; an exercise whose name matches an existing one gets its sets appended to it. This is the right tool for "log the set I forgot" or for logging a session in installments while the owner is still in the gym — which is how strength data actually arrives.

Reach for log_strength_entry ONLY when you intend the supplied exercises to become the day's ENTIRE session and everything else to be deleted.

date is the LOCAL calendar day the session happened, "YYYY-MM-DD". exercises is [{"name": "卧推", "sets": [{"weightKg": 30, "reps": 8}, ...]}, ...].

This tool deliberately cannot set the session note: that field is replace-only, and a tool that promises to delete nothing must not carry an exception. Use log_strength_entry to change it.

The result is the same shape log_strength_entry returns. replaced_exercises is always [] here — that empty list is the receipt that this call deleted nothing. Encrypted end-to-end before it ever leaves this machine. Requires a bind made after the agent write path shipped; an older bind must re-pair by running uvx vaultbeat-apple-health@latest bind in a terminal.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateYes
exercisesYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Annotations confirm non-destructive, non-idempotent write, and the description adds substantial context beyond them: matching-name exercises get their sets appended, notes cannot be set (replace-only), auth/pairing requirement is documented, and encrypted end-to-end is disclosed.

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 behavior and the sibling contrast. Slightly verbose in the encryption/auth paragraph, but every sentence carries information (the pairing requirement is 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?

Output schema exists and the description confirms `replaced_exercises` is always `[]`, explaining its semantic meaning. Auth prerequisites, side effects on existing data, and the note-field restriction are all covered — nothing important is missing.

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?

Schema coverage is 0%, so the description carries full burden — and it delivers: `date` format ('YYYY-MM-DD' LOCAL calendar day) and full example of the `exercises` array structure with nested sets (weightKg/reps).

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 ('add exercises to a session') and clearly contrasts the behavior with the sibling `log_strength_entry` (append vs. full replace). An agent can immediately tell this apart from `log_strength_entry` and `log_food_append`.

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 names when to use this tool ('log the set I forgot', logging in installments) and when NOT to use it ('Reach for `log_strength_entry` ONLY when...'), even describing the consequence of the wrong choice (everything else deleted).

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

log_strength_entryLog strength session (replaces day)A
Destructive

Log one strength-training session on the owner's behalf (agent write).

⚠️ THIS TOOL DELETES. The supplied exercises become the day's ENTIRE session — any exercise you don't re-send is silently deleted. If you meant to ADD to a day rather than replace it, STOP and call log_strength_append instead; it cannot delete anything.

(merge=True still does the same thing as log_strength_append and keeps working for callers that already use it. New callers should use the separate tool: which one you called is visible to the owner, a flag buried in the arguments is not.)

The result carries replaced_exercises — the names this call deleted. If that list is non-empty and you did not intend to replace the day, you just destroyed those exercises; re-send them with merge=True.

note=None LEAVES THE EXISTING NOTE ALONE (pass note="" to clear it).

date is the LOCAL calendar day the session happened, "YYYY-MM-DD". exercises is [{"name": "卧推", "sets": [{"weightKg": 30, "reps": 8}, ...]}, ...]. Encrypted end-to-end before it ever leaves this machine — this server never sends plaintext. Requires a bind made after this feature shipped (carries owner_user_id/owner_public_key_base64/owner_device_id from the pairing handshake); an older bind must re-pair by running uvx vaultbeat-apple-health@latest bind in a terminal.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateYes
noteNo
mergeNo
exercisesYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Annotations already flag destructiveHint=true, but the description goes far beyond them: it explains exactly what is destroyed (unlisted exercises silently deleted), surfaces the replaced_exercises result field as a verification signal, documents note=None vs note="" semantics, and discloses encryption plus a bind/version prerequisite with remediation.

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-loads the destructive warning before any procedural detail, which is exactly right. It is long, and the merge=True parenthetical and the re-pair remediation could be trimmed slightly, but nearly every sentence conveys operational information an agent needs.

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 destructive 4-parameter write with a nested payload, zero schema coverage, and a sibling that does the opposite, the description covers deletion semantics, verification via replaced_exercises, parameter edge cases, and the auth prerequisite. Nothing an agent needs to call it correctly is missing.

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?

Schema description coverage is 0%, so the description carries the full burden and does: date is the LOCAL calendar day in YYYY-MM-DD, exercises is given a concrete nested example, note=None preserves the existing note while note="" clears it, and merge's legacy behavior is explained.

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 ('Log one strength-training session') and immediately distinguishes itself from the sibling log_strength_append by declaring it REPLACES the day. An agent can tell the two tools apart 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 Guidelines5/5

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

Explicit when/when-not routing: 'If you meant to ADD to a day rather than replace it, STOP and call log_strength_append instead; it cannot delete anything.' It also explains the merge=True legacy path and why new callers should prefer the separate tool.

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

log_symptomLog symptomA

Record one symptom the person reports — one call per symptom (agent write).

This is where "我肚子疼" / "headache since lunch" / "拉肚子了" goes, not a note: each episode is kept as fields (type, severity, timing, place, suspected causes) so a later read can line it up with sleep, food, heart rate and wrist temperature. Two symptoms at once are two calls. This tool only ever adds; it cannot change or delete anything already recorded.

symptom_type: a short English token. Use the Apple Health name when one fits — abdominal_cramps, bloating, constipation, diarrhea, heartburn, nausea, vomiting, headache, dizziness, fatigue, fever, chills, coughing, sore_throat, runny_nose, lower_back_pain, chest_tightness_or_pain, shortness_of_breath, acne, night_sweats… — so it merges with what Apple Health imports; otherwise a plain descriptive one (rectal_bleeding, stomach_spasm, eye_strain). Any spelling is folded to one form. display_name: the person's own words, e.g. "便血" / "胃痉挛".

severity: mild | moderate | severe | unspecified. Use unspecified unless they said how bad it is — never grade it for them.

onset_at: when it began, ISO 8601 with their UTC offset, e.g. "2026-09-30T10:49+08:00". Omit it only for something happening NOW (it defaults to the current time). If they named a day but no time ("昨天头疼"), pass that day as a bare "2026-09-29" — do not invent an hour. end_at: when it eased, if they said; add it later with update_symptom. date: the local day to file it under, only when it differs from the onset's own day. body_location, note: free text, only what they said. triggers: what THEY suspect caused it, as short tokens (["bbq_dinner", "spicy_food"], ["sleep_deprivation"]). Record their suspicion; do not add causes of your own.

Returns entry_id (keep it for update_symptom / delete_symptom) and the entry as read back. Read everything with get_symptoms. Sealed for the owner and this machine only, end to end, before it leaves here.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNo
noteNo
end_atNo
onset_atNo
severityNounspecified
triggersNo
display_nameNo
symptom_typeYes
body_locationNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Annotations already declare destructiveHint=false and idempotentHint=false, but the description adds real context: it is strictly append-only ('cannot change or delete anything already recorded'), returns an entry_id to hand to update/delete, and is end-to-end sealed for the owner and machine. That is concrete behavior beyond the structured hints, with no contradiction.

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 purpose and the one-per-symptom rule, then organized field-by-field in visually scannable blocks using the exact parameter names. Given nine parameters and zero schema coverage the length is justified, though a few parenthetical asides (e.g. the trailing examples after the Apple Health list) could be trimmed.

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?

An output schema exists, so the description only needs to name the key return value, which it does ('entry_id ... and the entry as read back'). For a nine-parameter, 0%-schema-coverage mutation tool with no enums, the description closes every gap an agent would otherwise hit, including timezone handling and the do-not-invent severity/timing rules.

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?

Schema description coverage is 0%, so the description carries the full burden and does: it defines symptom_type naming conventions with merge rationale, supplies the severity enum (mild|moderate|severe|unspecified) that the schema lacks, and specifies ISO 8601 + UTC offset formats and edge-case defaults for onset_at, end_at, and date. It also constrains triggers to the user's own suspicion rather than invented causes.

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 ('Record one symptom the person reports') and immediately scopes the cardinality ('one call per symptom'). It explicitly distinguishes itself from log_note ('This is where ... goes, not a note') and names the sibling read/update/delete tools, so an agent can route without opening a schema.

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

Usage Guidelines5/5

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

Gives explicit when-to-use rules ('Two symptoms at once are two calls'), explicit when-not ('not a note'), and names the alternatives with their conditions (update_symptom to add an end time later, get_symptoms to read, delete_symptom to remove). Routing is fully determined by the text.

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

log_weight_entryLog weight (replaces day)A
Destructive

Log the owner's weight (kg) on their behalf (agent write, 2026-07-21).

weight_kg: kilograms (positive, ≤500). date: LOCAL calendar day "YYYY-MM-DD" (default = today). Same-day upsert-in-place semantics (dayID = "body-{dayStart.epoch}") — re-logging the same day overwrites. Encrypted end-to-end before it ever leaves this machine.

Written data always lands in Vaultbeat cloud + MCP (visible to get_metric series "weight_kg"). Whether it also reaches Apple Health depends on an iOS setting, OFF by default: the MCP tab → "Allow AI to update Apple Health" (app 1.2.8 and earlier: Settings → Data & AI). When it is on, weigh-ins logged here sync back into Apple Health on the next app sync.

⚠️ If the owner wants this number in the Apple Health app, tell them to turn that toggle ON — do NOT tell them to re-enter it by hand in the Vaultbeat weight card. Logging it in both places produces two entries for the same day from different sources and corrupts the trend line. (Before 2026-07-28 this docstring said propagation was impossible and instructed exactly that manual double-entry; the toggle shipped 2026-07-22.)

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNo
weight_kgYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Goes well beyond annotations: it discloses upsert-in-place keying (dayID = body-{dayStart.epoch}), end-to-end encryption before data leaves the machine, the dual write destination (Vaultbeat cloud + MCP, conditionally Apple Health), and a corruption failure mode from double-entry. The 'overwrites' semantics align with destructiveHint=true and the replace-day title.

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 actionable content is front-loaded (units, params, upsert semantics) before the propagation details. It loses a point for changelog-style asides — dated stamps like '(agent write, 2026-07-21)' and the parenthetical about the pre-2026-07-28 docstring — that document history rather than guide invocation.

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 mutation tool with an output schema already present, nothing an agent needs is missing: constraints, date semantics, overwrite behavior, encryption, downstream destinations, and the Apple Health toggle dependency are all stated. Return-value details are correctly left to the output schema.

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?

Schema description coverage is 0%, so the description must carry the load and it does: `weight_kg` is constrained (positive, ≤500) and `date` is defined as a LOCAL calendar day in 'YYYY-MM-DD' with a stated default of today. Neither constraint appears anywhere in the schema itself.

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 with units and actor: 'Log the owner's weight (kg) on their behalf'. It also distinguishes itself from the read path by naming the sibling surface (`get_metric` series "weight_kg") where the written data appears, so an agent can route write vs. read without opening schemas.

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 covers when to use it (agent-side weigh-in logging), what happens on repeat calls ('re-logging the same day overwrites'), and the alternative interaction pattern to avoid ('do NOT tell them to re-enter it by hand in the Vaultbeat weight card'). It also names the prerequisite for Apple Health propagation and the exact UI path to enable it.

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

update_symptomUpdate symptom (overwrites given fields)A
Destructive

Change fields of one reported symptom (agent write) — e.g. it eased.

The common call is update_symptom(entry_id, end_at="…") when the person says it has stopped, or a new severity when it got worse. Only the fields you pass change; omitted ones keep their value. Passing a field OVERWRITES it: note replaces the old note rather than adding to it, and triggers replaces the whole list — read the entry first and send the combined list if you mean to add one. "" clears a text field or a mistaken end_at; [] clears triggers.

entry_id comes from log_symptom or from get_symptoms' reported list. Symptoms imported from Apple Health cannot be edited here.

Returns previous — the old value of every field this call changed — and the entry as read back.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNo
noteNo
end_atNo
entry_idYes
onset_atNo
severityNo
triggersNo
display_nameNo
symptom_typeNo
body_locationNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Despite annotations covering the safety profile (destructiveHint=true, idempotentHint=false), the description adds critical behavior beyond annotations: overwrite semantics (note replaces rather than appends, triggers replaces the whole list), clearing conventions ('' and []), the read-back contract (returns previous plus entry as read back), and an edit restriction for Apple Health imports. This is exactly the extra context needed for a destructive, non-idempotent mutation.

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 common call and its trigger, then layers overwrite, clearing, sourcing, and return semantics. Every paragraph earns its place, though it runs several dense sentences and could tighten slightly.

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 10-param, 0%-coverage destructive mutation with an output schema, the description covers the partial-update model, overwrite/clear rules, id sourcing, edit exclusions, and return shape. Nothing an agent needs to call it correctly is missing.

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?

Schema coverage is 0%, so the description must carry parameter meaning. It supplies the critical partial-update semantics ('only the fields you pass change; omitted ones keep their value'), explains entry_id sourcing, and defines special values ('' clears text/end_at, [] clears triggers). This is substantially more than the bare schema titles.

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 (change fields) and resource (one reported symptom), clarifies it targets a single entry by entry_id, and differentiates from siblings like log_symptom and delete_symptom by noting it only modifies given fields.

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?

Gives concrete when-to-use examples ('when the person says it has stopped' → end_at, 'when it got worse' → severity) and explicitly excludes a case: symptoms imported from Apple Health cannot be edited here. Names the source tools for entry_id (log_symptom, get_symptoms).

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

vaultbeat_doctorRun diagnosticsA
Read-onlyIdempotent

Diagnose this Vaultbeat MCP install, and report which data types are unavailable.

Call this when a read tool returns nothing, or when anything fails, before telling the user their data is missing. Two distinct things come back:

checks — the install/binding chain: config present, keypair usable, binding valid, cloud reachable, a real record decryptable end to end. A failure here means the setup is broken, not that data is absent.

capabilities — which metric kinds actually have data, and which empty ones are explained by an older iOS app (with the release date each needs). An empty kind is NOT proof the user never recorded it: their app may predate the feature entirely. A kind under kinds_not_checked could not be counted this run; it is neither empty nor full.

scope — what this report does NOT cover. Everything here can pass and the setup still be broken on the client side: this server is a subprocess of your MCP client, so it cannot read the client's config file, cannot see which environment variables the client forwarded, and cannot tell whether it was launched with the arguments the user believes. A green report never clears the client. scope.env_overrides_received lists which Vaultbeat variables actually arrived — check that before guessing at the client's environment.

binding — the local pairing state (which account, which server, whether demo mode is serving synthetic data). No keys or tokens.

This runs a cloud round trip, so it takes a moment.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), yet the description adds substantial context: it performs a cloud round trip (latency warning), explains what each report section means, warns that a passing report does not clear the client side, and flags env_overrides_received as something to inspect. This is behavior the 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.

Conciseness4/5

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

Front-loaded with purpose, then cleanly sectioned with backticked field names (checks, capabilities, scope, binding). It is longer than typical, and some of the field-by-field explanation overlaps the output schema, but each section carries real diagnostic meaning rather than 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?

For a zero-arg diagnostic with an output schema and strong annotations, this is complete: it covers when to call it, what each result section means, how to interpret empty vs unchecked kinds, and the limits of a green report. An agent needs nothing else to invoke 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?

There are zero parameters, so the schema carries no parameter semantics to compensate for; baseline 4 applies. The description's detail about the report shape describes outputs (also covered by the output schema) rather than adding parameter meaning.

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 (diagnose), an explicit resource (this Vaultbeat MCP install), and a concrete deliverable (which data types are unavailable). It is unambiguously distinguishable from the read/log siblings, which fetch data rather than test the install.

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 precise trigger condition: 'Call this when a read tool returns nothing, or when anything fails, before telling the user their data is missing.' This routes the agent away from a specific failure mode. It does not name a particular sibling tool or state an explicit when-not, so it stops short of a 5.

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. 40 tool updatesv0.9.1
    • Changedcompare_metric_periods5 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / baseline
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Baseline"
        +}
      • removedInput schema / properties / owner
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "title": "Owner"
        -}
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / period
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Period"
        +}
    • Changedcorrelate_metric_series6 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / lag_days
        Added value: +{
        +  "default": 0,
        +  "title": "Lag Days",
        +  "type": "integer"
        +}
      • removedInput schema / properties / owner
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "title": "Owner"
        -}
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / since
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Since"
        +}
      • addedInput schema / properties / until
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Until"
        +}
    • Addeddelete_symptom
    • Removedget_activity
    • Removedget_basal_energy
    • Changedget_food_log6 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / limit / anyOf
        Added value: +[
        +  {
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / limit / default
        Previous value: -90New value: +null
      • removedInput schema / properties / limit / type
        Removed value: -"integer"
      • addedInput schema / properties / since
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Since"
        +}
      • addedInput schema / properties / until
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Until"
        +}
    • Removedget_hrv
    • Addedget_intraday
    • Changedget_menstrual_cycle3 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • removedInput schema / properties / owner
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "title": "Owner"
        -}
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
    • Addedget_metric
    • Changedget_metric_trend5 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • removedInput schema / properties / owner
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "title": "Owner"
        -}
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / since
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Since"
        +}
      • addedInput schema / properties / until
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Until"
        +}
    • Removedget_mindfulness
    • Changedget_notes2 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
    • Removedget_resting_hr
    • Changedget_sleep_detail8 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / limit / anyOf
        Added value: +[
        +  {
        +    "type": "integer"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / limit / default
        Previous value: -2New value: +null
      • removedInput schema / properties / limit / type
        Removed value: -"integer"
      • removedInput schema / properties / owner
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "title": "Owner"
        -}
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / since
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Since"
        +}
      • addedInput schema / properties / until
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Until"
        +}
    • Addedget_sleep_nights
    • Changedget_strength_log1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Changedget_symptoms2 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
    • Removedget_total_energy_burned
    • Addedget_user_profile
    • Removedget_vo2max
    • Removedget_water_intake
    • Removedget_weight_trend
    • Changedget_workouts2 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • removedInput schema / properties / owner
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "title": "Owner"
        -}
    • Removedget_wrist_temp
    • Changedlist_metric_series3 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / fresh
        Added value: +{
        +  "default": false,
        +  "title": "Fresh",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
    • Changedlog_food_append1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Changedlog_food_entry1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Changedlog_note2 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
    • Changedlog_note_append2 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / partner
        Added value: +{
        +  "default": false,
        +  "title": "Partner",
        +  "type": "boolean"
        +}
    • Changedlog_strength_append1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Changedlog_strength_entry1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Addedlog_symptom
    • Changedlog_weight_entry1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Addedupdate_symptom
    • Changedvaultbeat_doctor1 field changed
      • addedInput schema / additionalProperties
        Added value: +false
    • Removedvaultbeat_poll_binding
    • Removedvaultbeat_start_binding
    • Removedvaultbeat_status
    • Removedvaultbeat_sync_sleep
  2. 33 tool updatesv0.8.1
    • First observedcompare_metric_periods
    • First observedcorrelate_metric_series
    • First observedget_activity
    • First observedget_basal_energy
    • First observedget_food_log
    • First observedget_hrv
    • First observedget_menstrual_cycle
    • First observedget_metric_trend
    • First observedget_mindfulness
    • First observedget_notes
    • First observedget_resting_hr
    • First observedget_sleep_detail
    • First observedget_strength_log
    • First observedget_symptoms
    • First observedget_total_energy_burned
    • First observedget_vo2max
    • First observedget_water_intake
    • First observedget_weight_trend
    • First observedget_workouts
    • First observedget_wrist_temp
    • First observedlist_metric_series
    • First observedlog_food_append
    • First observedlog_food_entry
    • First observedlog_note
    • First observedlog_note_append
    • First observedlog_strength_append
    • First observedlog_strength_entry
    • First observedlog_weight_entry
    • First observedvaultbeat_doctor
    • First observedvaultbeat_poll_binding
    • First observedvaultbeat_start_binding
    • First observedvaultbeat_status
    • First observedvaultbeat_sync_sleep

TDQS

A4.3/5.0

Scored across 26 tools

Disambiguation5/5

The descriptions go out of their way to disambiguate overlapping tools: get_sleep_detail vs get_sleep_nights vs get_metric sleep series, log_X vs log_X_append (replace vs append), and get_symptoms vs get_notes vs log_symptom are all explicitly contrasted with 'which tool to use' sections. Every tool targets a clearly distinct resource+action, making misselection unlikely.

Naming Consistency4/5

Strong, predictable verb_noun snake_case throughout (get_*, log_*, update_*, delete_*, list_*, compare_*, correlate_*), with append/replace pairs named in a consistent pattern. The only deviation is 'vaultbeat_doctor', which breaks the verb_noun convention but is a single, clearly special-purpose tool.

Tool Count4/5

26 tools is at the upper end, but the domain is genuinely broad (sleep, metrics, symptoms, notes, food, strength, workouts, cycle, profile, plus analysis and diagnostics). Each tool maps to a distinct data kind or operation rather than duplicating surface, so the count is largely earned despite being slightly heavy.

Completeness5/5

The surface covers the full lifecycle: reads for every data kind, writes for symptoms (log/update/delete), notes, food, strength, and weight, plus analysis tools (trend, compare, correlate), discovery (list_metric_series), and diagnostics (doctor). Replace-vs-append pairs, partner reads, and coverage/limit handling leave no obvious dead ends.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers