Vaultbeat MCP Server
Vaultbeat MCP Server gives an AI agent locally-decrypted read and write access to your Apple Health data — sleep, cycle, HRV, workouts, food, strength, moods, symptoms and notes — plus server-side analysis, with everything encrypted end-to-end so only your machine sees plaintext.
Diagnose the install:
vaultbeat_doctorchecks config, key, cloud reachability, pairing and a real decrypt round trip, and reports which data types are empty and why.Read sleep:
get_sleep_nights(one compact row per night, a year per call),get_sleep_detail(per-stage intervals and per-stage HR/breathing rate for one or two nights).Read sensitive records:
get_menstrual_cycle(with next-period prediction),get_symptoms(Apple Health days plus self-reported episodes),get_state_of_mind(moods/valence),get_notes(day annotations).Read logs and workouts:
get_strength_log(exercises, sets × reps, volume),get_food_log(meals, items, optional kcal/macros),get_workouts,get_user_profile(sex, age, DOB, height).Daily metrics:
get_metricfor any series — steps, resting HR, HRV, weight/body composition, VO₂ max, water, energy, sleep stages and timing — with server-side aggregation (avg/sum/min/max/latest) and day/week/month/weekday granularity.Intraday and series discovery:
get_intradayfor within-day HRV samples;list_metric_seriesto see every available series and how much data backs it.Analysis:
get_metric_trend(least-squares slope),compare_metric_periods(newest N vs prior N, or two named windows),correlate_metric_series(Pearson r, with optional lag) — numbers only, no verdicts, and refuses on too few points.Write data: append-only
log_food_append,log_strength_append,log_note_append,log_symptom,log_weight_entry; replace-whole-daylog_food_entry,log_strength_entry,log_note(all report exactly what was removed);log_state_of_mind.Correct writes:
update_symptom,delete_symptom,delete_state_of_mind.Partner reads: sleep, water, weight and (if the partner opts in) cycle, symptoms, moods and notes via
partner=true, kept strictly separate from your own data; notes about your partner stay in your account.Serves 8 MCP prompts (e.g.
daily_brief,sleep_review,energy_balance,why_is_this_empty) that name the tools to call and the coverage/empty-result rules.Run anywhere: stdio or HTTP transport with bearer-token auth, plus a
--demosynthetic dataset for trying it without an iPhone.Coverage-aware results: every read reports
coverage.days_covered,more_availableandoldest_available, so a limit-shaped window is never mistaken for the extent of your history.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Vaultbeat MCP ServerHow did we sleep last night?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Vaultbeat Apple Health MCP Server
Your Apple Health data — sleep stages, cycle, HRV, resting heart rate, workouts, weight, VO₂ max, meals, lifts, notes, symptoms, moods (State of Mind) — 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 agentThe 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+, anduvxdownloads 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 bindIt 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, as a plugin (adds the server and a setup guide that walks Claude through pairing):
/plugin marketplace add Fino-wind/vaultbeat-apple-health
/plugin install vaultbeat-apple-health@vaultbeatThen run /vaultbeat-apple-health:setup. Or add only the server:
claude mcp add vaultbeat-health -- uvx vaultbeat-apple-health@latest serve --transport stdioClaude 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 doctorIt 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
29 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, moods 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 |
| 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 |
| 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; |
| One or two nights in depth: stage intervals with per-stage heart and breathing rate. The newest nights, or any night by date ( |
| Cycle samples and a next-period prediction. Sensitive. |
| Symptoms from Apple Health, and beside them the episodes a person reported (type, severity, onset and end, place, suspected triggers). Sensitive. |
| Moods — Apple Health's State of Mind: how pleasant each feeling was (valence and Apple's seven regions), Apple's feeling words and life areas, from the Watch, the Health app or other apps, and the ones an AI recorded; per-day count and average. The 100 newest by default, or a |
| Free-text notes on days, with who wrote them. Sensitive. |
| Strength sessions: exercises, sets × reps, volume per session. |
| 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 |
| Workouts: type, duration, calories, distance. |
| Sex, age, date of birth and height. Needs Vaultbeat for iOS 1.2.9 or later. |
Daily numbers and analysis
Tool | What it returns |
| 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. |
| Samples within a day, for HRV: one row per hour that has samples ( |
| Every series name the tools above and below accept, with its unit and how much data backs it. |
| Least-squares slope, endpoints, mean / median / min / max. |
| The newest N days against the N before them, or two named windows. |
| Pearson r between two series over the days that have both; |
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 |
| Add to a day. Cannot delete anything — the safe default when a day may already have entries. |
| Replace that whole day with what is passed, and report exactly what was removed. |
| Record a weigh-in, keeping that day's body composition. |
| Record one symptom the person reports, as structured fields a later read can match against sleep, food and heart rate. |
| Change a reported symptom (usually its end time), or erase one logged by mistake. |
| Record how the person says they feel, in Apple Health's State of Mind vocabulary (valence, feeling words, life areas). Their iPhone writes it into Apple Health only if they turned on "Allow AI to update Apple Health" (iOS app 1.3.0 or later). |
| Erase a mood an AI recorded by mistake; the iPhone also removes the copy it wrote into Apple Health. |
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
coverageblock.days_coveredis how many distinct days the answer rests on — quote it beside any average or trend.more_available: truemeans older records exist and yourlimitstopped short of them (oldest_availablesays 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_largewith a suggestion for narrowing the request (a smallerlimit, asincedate, one series instead of all).get_metricandget_intradayreturn 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, your moods (State of Mind, from Apple Health and from
log_state_of_mind) and the symptoms you report yourself (in the app, or throughlog_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:
the
VAULTBEAT_PRIVATE_KEYenvironment variable, if set (read, never written back) — for operators injecting it from a secret store;the system keyring — the normal case on a desktop;
~/.tether/mcp-local/identity.key, mode0600— 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.jsonto "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, runbindagain: 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 requiredThe 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 srcvaultbeat-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
29 toolscompare_metric_periodsCompare two periodsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| fresh | No | ||
| period | No | ||
| series | Yes | ||
| partner | No | ||
| baseline | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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 seriesARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| fresh | No | ||
| since | No | ||
| until | No | ||
| partner | No | ||
| lag_days | No | ||
| series_a | Yes | ||
| series_b | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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_state_of_mindDelete state of mind (erases the entry)ADestructive
Erase one state of mind an AI recorded by mistake (agent write).
Only for an entry that should never have existed — a duplicate, the wrong reading of what they said, the wrong person. To record that a feeling changed, log a new one; feelings change and both are true.
It disappears from get_state_of_mind at once. The person's iPhone, on
its next sync, drops it from the app, overwrites its content in the cloud
for every reader it was shared with, and removes the sample it wrote into
Apple Health, if it wrote one. The result carries
deleted_entry — the full entry as it was — so a mistaken delete can be
logged again. Records with source: "apple_health" cannot be deleted
here; they are edited in Apple Health.
| Name | Required | Description | Default |
|---|---|---|---|
| entry_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the destructiveHint=true annotation by disclosing the full cascade: immediate removal from get_state_of_mind, next-sync removal from the iPhone app, cloud content overwrite for all shared readers, and Apple Health sample deletion. It also notes the deleted_entry payload enabling re-logging and the apple_health source restriction — rich 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core intent and the when-to-use condition, then layered with consequences. Slightly long, but nearly every sentence adds distinct operational value (cascade effects, return payload, source restriction).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need not be enumerated, yet the description still flags the deleted_entry payload. Combined with the usage exclusions, cascade effects, and source restriction, an agent has everything needed to invoke this destructive tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the description never explains the entry_id parameter or where to obtain it. It references related structures (get_state_of_mind, source values) but does not add semantics for the single required parameter, so the schema underspecification is only partially compensated.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Erase one state of mind'), scopes it as an agent write, and immediately distinguishes the intent from the sibling log_state_of_mind. An agent can identify the operation 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states the only valid use case (an entry that should never have existed — duplicate, wrong reading, wrong person) and names the alternative behavior ('To record that a feeling changed, log a new one'). It also gives an exclusion: apple_health-sourced records 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.
delete_symptomDelete symptom (erases the entry)ADestructive
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=…).
It disappears from get_symptoms at once, and from the app and every
other reader once the person's iPhone next syncs (it then overwrites the
entry's own copy in the cloud, and from app 1.3.0 removes the copies
earlier update_symptom calls left; an older app leaves those sealed
there, readable only by the person and the server that wrote them). 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.
| Name | Required | Description | Default |
|---|---|---|---|
| entry_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, readOnlyHint=false, and idempotentHint=false, so safety is covered structurally. Beyond that, the description discloses propagation timing (vanishes from get_symptoms immediately, from the app and other readers after the next iPhone sync), a version-dependent side effect on earlier update_symptom copies, and the returned deleted_entry payload that allows re-logging. This is unusually rich behavioral context that annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core decision (when to delete vs. not) and the alternative are front-loaded before the sync-propagation detail. The long parenthetical about app 1.3.0 and sealed copies is dense but does describe a real behavioral caveat, so it earns its place even if it could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive tool with an output schema, this covers the destructive scope, the path to recover a mistaken delete, the sibling to use instead, and a hard exclusion (Apple Health imports). An agent has everything needed to decide and invoke correctly; return values need not be explained further given the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for the single entry_id parameter, so the schema does not define it. The description references entry_id by name in the sibling example but never states what identifies it, its format, or how to obtain it (presumably from get_symptoms). It adds partial context but does not fully compensate for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Erase one reported symptom'), constrains scope ('that was logged by mistake'), and explicitly carves out the boundary against the nearest sibling by naming update_symptom as the tool for symptoms that ended. An agent can distinguish this from log_symptom and update_symptom 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use cases (duplicate, wrong person, symptom they did not have), an explicit when-NOT case ('a symptom that simply ended is NOT deleted'), the alternative to use instead with exact call syntax (update_symptom(entry_id, end_at=…)), and an additional exclusion (Apple Health imports cannot be deleted here). 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.
get_food_logFood logARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| since | No | ||
| until | No | ||
| limit_days | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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 samplesARead-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 itssample_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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| series | No | hrv_sdnn | |
| granularity | No | hourly |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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 cycleARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| partner | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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 metricsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| fresh | No | ||
| since | No | ||
| until | No | ||
| series | No | ||
| partner | No | ||
| aggregation | No | none | |
| granularity | No | day |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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 trendARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| fresh | No | ||
| since | No | ||
| until | No | ||
| series | Yes | ||
| partner | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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_notesNotesARead-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), and "day" is what the
person wrote about the day itself in the app's notes list (app 1.3.0+);
"general" is
agent-authored via log_note; "mood" notes are ones an AI wrote before
moods moved to get_state_of_mind — read them here, write no new ones.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| partner | No | ||
| target_kind | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/idempotent/closed-world annotations: states data stays on-device and is never re-exported, flags the content as SENSITIVE free text, and warns about the coverage-block semantics (days_covered vs row count, window_satisfied ambiguity, more_available meaning days older than first_day exist). This is exactly the behavioral 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and every section (kinds, partner scope, coverage warnings) adds decision-relevant information, so the length is largely earned. Structure suffers from awkward mid-sentence line breaks and some density, but there is little true filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still explains the coverage block an agent must quote correctly and the more_available trap that prevents misreporting history extent. Combined with the kind taxonomy and partner scoping, an agent has everything needed to call and interpret this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must carry parameter meaning and largely does: it enumerates the target_kind values and their provenance, defines partner=true, and implies limit's role via the 'limit-shaped window' warning and 're-read with a larger limit'. The fresh parameter is never explained, which is the one gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Decrypt recent free-text notes (day annotations) locally') and immediately distinguishes itself from siblings by explaining that mood notes live in get_state_of_mind and new notes are written via log_note/log_note_append. An agent can tell exactly what this tool retrieves and what it does not.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear routing context: pass target_kind to filter, use partner=true for partner-authored/partner-about notes, read legacy mood notes here but write no new ones. It stops short of a crisp when-to-use-vs-alternative rule (e.g. no explicit 'use get_notes vs get_state_of_mind when...'), but the implicit guidance is strong.
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 detailARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| since | No | ||
| until | No | ||
| partner | No | ||
| include_timeline | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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 nightARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| since | No | ||
| partner | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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_state_of_mindState of mind (moods)ARead-onlyIdempotent
Decrypt recent states of mind (how the person felt) locally — both sources.
SENSITIVE. Apple Health's "State of Mind": each record is one feeling with
valence (-1 very unpleasant … 0 neutral … +1 very pleasant),
valence_classification (Apple's seven regions, veryUnpleasant …
veryPleasant — use these words rather than inventing a grade), labels
(Apple's feeling words: drained, stressed, calm, grateful…),
associations (the life area: work, family, partner, health…), kind
(momentaryEmotion = how they felt at that moment, dailyMood = how the
day went overall), local_date / local_time.
source says where it came from:
· "apple_health" — logged by the person on their Watch, in the Health app
or in another app, and imported by their iPhone (app names it);
· "ai" — recorded by an AI through log_state_of_mind (entry_id,
note). Remove a mistaken one with delete_state_of_mind.
Each owner also carries daily (per day: count, mean_valence,
lowest_valence, highest_valence, daily_mood_valence) and counts per
region, label and life area. Describe what was logged; a day with one
record says far less than a day with ten — quote count beside any
average, and never read a day with no record as a neutral day.
By default the newest 100 records (about three months of daily moods).
since / until ("YYYY-MM-DD", local days, both inclusive) read a
calendar window instead — "how was I in March", or anything older than
the default page — and then limit applies only if you pass it. A
record with its tallies is roughly 450 bytes, so about 130 fit in one
result; a longer window comes back as result_too_large — read it a
month or a quarter at a time.
Returns YOUR records by default; partner=true returns your partner's —
only if they turned on sharing their moods with your AI in their own
Vaultbeat app (off by default). Stays on-device, never re-exported.
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 records OLDER than first_day that your read left behind —
read them with a since / until window ending before first_day (a much
larger limit does not fit in one result), or quote
coverage.oldest_available as the real start of their history. Never report a
limit-shaped window as the extent of their data.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| since | No | ||
| until | No | ||
| partner | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish the safe-read profile (readOnly, idempotent, non-destructive, closed world), and the description goes well beyond them: it flags the data as SENSITIVE, states it stays on-device and is never re-exported, discloses the `result_too_large` failure mode with its size math, and explains the `coverage` block, `window_satisfied`, and `more_available` semantics so the agent does not misreport history extent. This is unusually rich behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the dense body is mostly load-bearing (window semantics, coverage interpretation, size limits). It is long and occasionally repetitive — the coverage/more_available guidance restates itself across several sentences — but almost every line changes how the agent should call or interpret the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shape needn't be spelled out, yet the description still supplies the interpretive context an agent needs (per-record fields, per-owner daily tallies, the warning not to treat a record-less day as neutral, and how to quote `count` and `coverage` beside averages). For a multi-source, paged, sensitive read tool this is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description carries the full burden, and it does so for four of five params: `since`/`until` format and inclusivity, `limit`'s conditional application, and `partner`'s sharing precondition. The `fresh` parameter is never mentioned in either the schema or the description, leaving one gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Decrypt recent states of mind') with an immediate gloss of what the resource means ('how the person felt'), and distinguishes itself from the write path by naming `log_state_of_mind` and `delete_state_of_mind` as the tools for recording/removing. An agent can separate this read tool from its siblings without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly covers when to use the default page (newest 100), when to switch to a `since`/`until` window ('how was I in March', data older than the default page), how `limit` interacts with a window, how to page around the ~130-record result cap, and the condition for `partner=true`. It also routes removal to `delete_state_of_mind`. 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.
get_strength_logStrength logARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| limit_days | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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_symptomsSymptomsARead-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. limit cuts each list on its own: that many imported days AND that
many reported entries, so a day with three reports can come back with one.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No | ||
| partner | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish a safe read (readOnly, idempotent, non-destructive), and the description adds substantial context beyond them: sensitive on-device data never re-exported, partner sharing semantics ('empty reported means not shareable, not none logged'), coverage-block caveats, and the warning that each list is cut independently by `limit`. This is exactly the behavioral depth the dimension rewards.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose and uses bulleted structure to separate the two data sources, with the load-bearing limit caveat at the end. It is dense and long, but nearly every sentence carries actionable information; only the repetitive coverage-block explanation could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists, yet the description still supplies the interpretive schema the agent needs: field-level meaning, count semantics (days vs episodes), the coverage quotation rule, and the limit-trap warning. For a sensitive, multi-source, share-gated tool, nothing essential to correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must carry all parameter meaning. It richly explains `partner=true` and the per-list `limit` behavior (including the 'one day can return one report' warning), but `fresh` is never mentioned, leaving one of three parameters undocumented in both schema and description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Decrypt recent symptoms locally, grouped by data owner') and immediately distinguishes the two sources (Apple Health 'days' vs self- 'reported' episodes). An agent can tell it apart from log_symptom/update_symptom/delete_symptom, which are explicitly named as the write/delete path.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear context: it routes corrections/removals to update_symptom/delete_symptom, explains that imported days are edited in Apple Health, and defines the partner=true scope. It lacks an explicit 'use this when...' trigger and does not explain the `fresh` flag's role, so it stops short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_user_profileHealth profileARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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_workoutsWorkoutsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| limit | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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 seriesARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fresh | No | ||
| partner | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | ||
| meals | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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)ADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | ||
| note | No | ||
| meals | Yes | ||
| merge | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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)ADestructive
Log a free-text note on the owner's behalf (agent write).
For day events that belong next to the metric data instead of in chat
history, worth joining against sleep/HRV later (kind="general", the
default and the only kind this tool writes).
A feeling does NOT go here, nor the story behind it: call
log_state_of_mind, which keeps it as Apple Health's fields and has its
own note for the why. This tool refuses kind="mood" — one feeling
recorded twice, once structured and once as free text, is what that kind
produced. (sleep, menstrual and day notes are written in the iOS app —
refused too.)
⚠️ 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 (events, how a day
went).
date = LOCAL calendar day "YYYY-MM-DD" (default today). Read back via
get_notes (optionally target_kind="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 for you and your own
AI machines only (the Vaultbeat app passes it on to your other paired
machines) — never for your partner or their AI. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ||
| kind | No | general | |
| text | Yes | ||
| merge | No | ||
| partner | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, but the description goes far beyond: it explains the one-note-per-(kind, day) model, that text becomes the ENTIRE contents and prior text is silently deleted, that replaced_text in the result signals an unintended destruction, and that content is end-to-end encrypted before leaving the machine.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and the destructive warning are front-loaded and the prose is organized into scannable blocks, but it is long and repeats itself — the replace-vs-append danger is stated in the warning, again in the merge paragraph, and again in the replaced_text paragraph.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, five-parameter write tool this covers everything an agent needs: routing to siblings, destruction semantics, date format, partner isolation, read-back path via get_notes, and recovery guidance. An output schema exists, yet the description still usefully calls out the one field that matters (replaced_text).
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description carries the full burden and does: date format and default, kind default and the only accepted value, text's replace-not-append semantics, merge's back-compat behavior, and partner's storage/isolation semantics. All five parameters are given meaning beyond their bare names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource up front ('Log a free-text note on the owner's behalf') and immediately narrows scope: only kind='general' is written. It explicitly names what this tool is not (log_state_of_mind, log_symptom, log_note_append), 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use and when-not-to-use rules: feelings go to log_state_of_mind, symptoms to log_symptom, additive writes to log_note_append, and kind='mood'/sleep/menstrual/day notes are refused. It even explains the routing mechanism (separate tool vs buried flag) and which is visible to the owner.
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" (the default and the only kind it writes) for day events
worth joining against sleep/HRV later. A feeling and the story behind it
go to log_state_of_mind and its note; this tool refuses kind="mood",
and sleep, menstrual and day notes are written in the iOS app. 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 for you and your own
AI machines only (the Vaultbeat app passes it on to your other paired
machines) — never for your partner or their AI. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ||
| kind | No | general | |
| text | Yes | ||
| partner | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description clearly discloses that the tool appends and cannot delete existing content, that `replaced_text` is always `null` as a receipt of no deletion, and that data is encrypted end-to-end. It also explains the `partner=true` storage and visibility model in concrete terms, which is valuable behavioral context not captured by the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is front-loaded with the key append-versus-replace distinction, and the remaining paragraphs add useful routing and behavioral context. It is longer than typical, but most of the length is justified by the number of sibling tools and the non-obvious partner/encryption semantics.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has an output schema, so the description need not fully document return values, but it still notes that `replaced_text` is always `null` here. Combined with the explicit alternative-routing guidance and complete parameter explanations, an agent has enough information to call this tool correctly without opening sibling schemas.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does: it defines `date` as a local calendar day in `YYYY-MM-DD` format, explains `kind="general"` as the default and only writable kind while refusing `kind="mood"`, and fully specifies `partner=true` versus default behavior. It also makes clear that `text` is appended as a new line.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: it appends text to a day's note without erasing existing content. It also explicitly distinguishes this append behavior from `log_note`, which replaces the note, so an agent can choose correctly without inspecting either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use and when-not-to-use guidance, including telling the agent to use `log_note` only when replacing the entire note and to use `log_symptom` for symptoms. It further routes feelings to `log_state_of_mind`, explains the `kind="general"` default, and clarifies partner-note behavior.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
log_state_of_mindLog state of mindA
Record how the person says they feel, as Apple Health's State of Mind (agent write).
This is where "今天工作好累,被掏空了" / "刚散步回来,心情特别平静" /
"I'm so anxious about tomorrow" goes when they tell you how they feel —
people rarely open an app to log a feeling, but they say it to you. One
call per feeling. This tool only ever adds; delete_state_of_mind
removes a mistaken one.
valence: -1 (very unpleasant) … 0 (neutral) … +1 (very pleasant), YOUR
reading of how pleasant it felt FROM THEIR WORDS. Apple splits the range
into seven equal regions: about -0.85 very unpleasant, -0.55 unpleasant,
-0.3 slightly unpleasant, 0 neutral, +0.3 slightly pleasant, +0.55
pleasant, +0.85 very pleasant. Stay inside what they said; do not
dramatise "有点烦" into very unpleasant.
labels: Apple's feeling words that THEIR words support, usually one to
three — amazed, amused, angry, annoyed, anxious, ashamed, brave, calm,
confident, content, disappointed, discouraged, disgusted, drained,
embarrassed, excited, frustrated, grateful, guilty, happy, hopeful,
hopeless, indifferent, irritated, jealous, joyful, lonely, overwhelmed,
passionate, peaceful, proud, relieved, sad, satisfied, scared, stressed,
surprised, worried. Leave it empty rather than guess when they only said
"心情还行".
associations: what it is about, only if they said — community,
current_events, dating, education, family, fitness, friends, health,
hobbies, identity, money, partner, self_care, spirituality, tasks,
travel, weather, work.
kind: "momentaryEmotion" (default — how they feel right now or at one
moment) or "dailyMood" (how the whole day has been).
logged_at: the moment, ISO 8601 with their UTC offset, e.g.
"2026-10-04T14:20+08:00"; omit it for right now. A dailyMood about a past
day may give just that day as date ("2026-10-03") — do not invent an hour.
note: a short summary in their words, only if useful later. It stays in
Vaultbeat; Apple Health has no place for text.
The person's iPhone writes it into Apple Health (Mental Wellbeing → State
of Mind) only if they turned on "Allow AI to update Apple Health" in the
app — it is off by default; either way it shows in Vaultbeat's mood
calendar (app 1.3.0 and later; an older app keeps it without showing it). Returns entry_id (for delete_state_of_mind) and the entry as
read back. Read everything with get_state_of_mind. Encrypted end to end
before it leaves here, for the owner and this machine; the owner's iPhone
then re-seals it for their other paired machines and — only if they turned
on sharing moods with their partner's AI — for that AI too, note
included, so write nothing in note they would not want that reader to see.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ||
| kind | No | momentaryEmotion | |
| note | No | ||
| labels | No | ||
| valence | Yes | ||
| logged_at | No | ||
| associations | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare non-read-only, non-destructive, non-idempotent; the description adds the append-only guarantee ('only ever adds'), the iPhone write gate that is off by default, the app-version caveat for the mood calendar, the returned `entry_id`, and the encryption/partner-sharing implications of `note`. That privacy warning about who can read `note` is exactly the kind of context structured fields 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and trigger come first, then per-parameter guidance, then a caveats paragraph — a sensible front-loaded order with no filler sentences. It is long, and the multi-line example and encryption sentences are dense, but nearly every line carries operational information an agent would otherwise get wrong.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
A seven-parameter write tool with a 0%-coverage schema, a 0% description deficit would be fatal here, and none exists; return values are backed by an output schema and are nevertheless summarized (`entry_id` plus the entry as read back). Write-gating, privacy of `note`, and the delete counterpart are all present, leaving nothing essential to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry all seven parameters — and it does: valence gets the -1..+1 scale plus Apple's seven-region mapping and an anti-dramatisation rule, labels and associations get controlled vocabularies with a 'leave it empty rather than guess' rule, kind gets both allowed values with their meanings, and logged_at/date get concrete ISO formats and a 'do not invent an hour' constraint.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb+resource ('Record how the person says they feel, as Apple Health's State of Mind') and anchors it with realistic utterance examples that map directly to the trigger condition. It is trivially distinguishable from `get_state_of_mind` (read) and `delete_state_of_mind` (remove a mistaken one), both of which are named.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use ('when they tell you how they feel'), why it matters ('people rarely open an app to log a feeling'), the cadence ('one call per feeling'), and the corrective path via `delete_state_of_mind`. It also points to `get_state_of_mind` for reading back, covering the alternative-selection burden completely.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | ||
| exercises | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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)ADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | ||
| note | No | ||
| merge | No | ||
| exercises | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ||
| note | No | ||
| end_at | No | ||
| onset_at | No | ||
| severity | No | unspecified | |
| triggers | No | ||
| display_name | No | ||
| symptom_type | Yes | ||
| body_location | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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)ADestructive
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.
A weight's home is Apple Health: the phone carries this one there on its
next sync, through the iOS setting "Allow AI to update Apple Health" (MCP
tab; OFF by default). It is also readable here at once (get_metric
series "weight_kg").
⚠️ When no iPhone of this account has that setting on (with Weight access granted), the write is REFUSED and nothing is saved — from app 1.3.0 a weight may not live only in the cloud. The error says what to turn on; pass it on and do not retry until the person has done it. Never tell them to re-enter the number by hand in the Vaultbeat weight card instead: logging it in both places makes two entries for one day from different sources and corrupts the trend line.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ||
| weight_kg | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (destructiveHint=true, readOnlyHint=false): same-day upsert-in-place with the dayID scheme, end-to-end encryption before data leaves the machine, the Apple Health sync side effect gated by a default-OFF iOS setting, the hard refusal condition when the setting is off, and the corruption risk of duplicate entry. This is exactly the behavioral context annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and parameter semantics before the longer environmental caveats, so the critical path is readable first. It is longer than the two-parameter schema strictly requires, and internal details like 'dayID = "body-{dayStart.epoch}"' add length without helping the agent decide or call.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive same-day upsert with an output schema already present, the description covers everything an agent needs: the mutation and its overwrite behavior, the prerequisite iOS setting, the failure mode and required error handling, and the read path. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the load and does: weight_kg is kilograms, positive, ≤500; date is a LOCAL calendar day in 'YYYY-MM-DD' format defaulting to today. Both parameters are given units, format, bounds, and default semantics that the raw schema lacks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: logging the owner's weight (kg) on their behalf, with the title additionally marking it as a day-replacing write. It is clearly distinguishable from siblings such as log_strength_entry, log_food_entry, and log_symptom 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-not guidance: when no iPhone has the Apple Health setting on, the write is refused, the agent should pass the error on and not retry until the user acts. It also names the correct alternative path (read via get_metric 'weight_kg', phone syncs to Apple Health) and forbids the wrong workaround (re-entering by hand in the Vaultbeat weight card).
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)ADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ||
| note | No | ||
| end_at | No | ||
| entry_id | Yes | ||
| onset_at | No | ||
| severity | No | ||
| triggers | No | ||
| display_name | No | ||
| symptom_type | No | ||
| body_location | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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 diagnosticsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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.
3 tool updates
v0.9.3- Added
delete_state_of_mind - Added
get_state_of_mind - Added
log_state_of_mind
40 tool updates
v0.9.1- Changed
compare_metric_periods5 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / baselineAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline" +} - removed
Input schema / properties / ownerRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Owner" -} - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +} - added
Input schema / properties / periodAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Period" +}
- Changed
correlate_metric_series6 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / lag_daysAdded value: +{ + "default": 0, + "title": "Lag Days", + "type": "integer" +} - removed
Input schema / properties / ownerRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Owner" -} - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +} - added
Input schema / properties / sinceAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Since" +} - added
Input schema / properties / untilAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Until" +}
- Added
delete_symptom - Removed
get_activity - Removed
get_basal_energy - Changed
get_food_log6 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / limit / anyOfAdded value: +[ + { + "type": "integer" + }, + { + "type": "null" + } +] - changed
Input schema / properties / limit / defaultPrevious value: -90New value: +null - removed
Input schema / properties / limit / typeRemoved value: -"integer" - added
Input schema / properties / sinceAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Since" +} - added
Input schema / properties / untilAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Until" +}
- Removed
get_hrv - Added
get_intraday - Changed
get_menstrual_cycle3 fields changed- added
Input schema / additionalPropertiesAdded value: +false - removed
Input schema / properties / ownerRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Owner" -} - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +}
- Added
get_metric - Changed
get_metric_trend5 fields changed- added
Input schema / additionalPropertiesAdded value: +false - removed
Input schema / properties / ownerRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Owner" -} - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +} - added
Input schema / properties / sinceAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Since" +} - added
Input schema / properties / untilAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Until" +}
- Removed
get_mindfulness - Changed
get_notes2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +}
- Removed
get_resting_hr - Changed
get_sleep_detail8 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / limit / anyOfAdded value: +[ + { + "type": "integer" + }, + { + "type": "null" + } +] - changed
Input schema / properties / limit / defaultPrevious value: -2New value: +null - removed
Input schema / properties / limit / typeRemoved value: -"integer" - removed
Input schema / properties / ownerRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Owner" -} - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +} - added
Input schema / properties / sinceAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Since" +} - added
Input schema / properties / untilAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Until" +}
- Added
get_sleep_nights - Changed
get_strength_log1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
get_symptoms2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +}
- Removed
get_total_energy_burned - Added
get_user_profile - Removed
get_vo2max - Removed
get_water_intake - Removed
get_weight_trend - Changed
get_workouts2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - removed
Input schema / properties / ownerRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Owner" -}
- Removed
get_wrist_temp - Changed
list_metric_series3 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / freshAdded value: +{ + "default": false, + "title": "Fresh", + "type": "boolean" +} - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +}
- Changed
log_food_append1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
log_food_entry1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
log_note2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +}
- Changed
log_note_append2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / partnerAdded value: +{ + "default": false, + "title": "Partner", + "type": "boolean" +}
- Changed
log_strength_append1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
log_strength_entry1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Added
log_symptom - Changed
log_weight_entry1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Added
update_symptom - Changed
vaultbeat_doctor1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Removed
vaultbeat_poll_binding - Removed
vaultbeat_start_binding - Removed
vaultbeat_status - Removed
vaultbeat_sync_sleep
33 tool updates
v0.8.1- First observed
compare_metric_periods - First observed
correlate_metric_series - First observed
get_activity - First observed
get_basal_energy - First observed
get_food_log - First observed
get_hrv - First observed
get_menstrual_cycle - First observed
get_metric_trend - First observed
get_mindfulness - First observed
get_notes - First observed
get_resting_hr - First observed
get_sleep_detail - First observed
get_strength_log - First observed
get_symptoms - First observed
get_total_energy_burned - First observed
get_vo2max - First observed
get_water_intake - First observed
get_weight_trend - First observed
get_workouts - First observed
get_wrist_temp - First observed
list_metric_series - First observed
log_food_append - First observed
log_food_entry - First observed
log_note - First observed
log_note_append - First observed
log_strength_append - First observed
log_strength_entry - First observed
log_weight_entry - First observed
vaultbeat_doctor - First observed
vaultbeat_poll_binding - First observed
vaultbeat_start_binding - First observed
vaultbeat_status - First observed
vaultbeat_sync_sleep
TDQS
Scored across 29 tools
Most tools target a distinct resource+action, and the descriptions include explicit 'which tool to use' guidance (e.g. get_sleep_detail vs get_sleep_nights vs get_metric, or get_metric vs get_intraday). However, the deliberate log_X_entry (replace) vs log_X_append (add) pairs, plus the many read tools over overlapping metric data, create genuine adjacency that an agent could misselect without reading the long caveats.
Names follow a strict verb_noun snake_case convention throughout: get_*, log_*, update_*, delete_*, list_*, compare_*, correlate_*. The replace/append split is expressed consistently across note, strength, and food domains, with vaultbeat_doctor as the only (understandable) outlier.
29 tools is heavy and sits at the edge of the 'too many' band for a single server. The health domain is broad enough that each tool is justified (multiple data types, read/append/replace variants), but the surface is large enough that an agent must navigate a lot of near-adjacent options.
Broad lifecycle coverage: symptoms, state of mind, notes, strength, food, weight, and metrics all have create/read (+update or append/delete) paths, and Apple-Health-sourced kinds are correctly read-only. Gaps are minor — no delete for food/strength/notes (only replace/append) — and are largely explained as intentional given the app as source of truth.
Maintenance
Related MCP Connectors
Your Apple Health data, food diary and lifts, for ChatGPT, Claude and Gemini. Approved on iPhone.
Read-only Apple Health data synced from your iPhone: sleep, heart rate, HRV, workouts and trends.
Connect your Apple Health workouts, sleep and training progress to your AI assistant.
Connect your health, fitness, nutrition, sleep, and wearable data to your AI assistant.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI to read-only access Apple Health data (current status, sleep, trends) via a Cloudflare-deployed MCP server, using iPhone shortcuts to sync data.MIT
- AlicenseNot gradedqualityCmaintenanceProvides AI with read-only access to Apple Health data via three tools (health_now, health_detail, health_trends). Deployable on Cloudflare with a one-click mobile setup.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to read-only access Apple Health data through three tools: current status and sleep, detailed metrics, and 7/14/30-day trends, deployed via one-click on Cloudflare with private-key authentication.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to read Apple Health data through three tools: current stats, detailed metrics, and trends. Deployable to Cloudflare with a one-click phone setup, exposing only read-only access.MIT