New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@reneturcios/healthfolder

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@reneturcios/healthfolder

Read a Health Access folder of Apple Watch workouts and vitals as plain files, and write training plans back into the same folder. Local stdio MCP server and CLI; the folder is the API, no account, no network.

latest
Source
npmnpm
Version
0.1.0
Version published
Maintainers
1
Created
Source

healthfolder

Read a Health Access folder of Apple Watch workouts and vitals, and write training plans back into it — as plain files. The folder an app writes from Apple Health (workouts, day files, an index, a profile) is the whole API: this package is a local stdio MCP server plus a CLI over that folder. No account, no network, nothing leaves the machine.

How the folder is found

First hit wins:

  • --dir <folder> on the command line
  • the HEALTHACCESS_DIR environment variable (alias TIDE_DIR)
  • dir = "..." in ~/.config/healthaccess/config.toml (the path can be overridden with HEALTHACCESS_CONFIG; zone bounds can also be set there as zones = [133, 146, 158, 169])
  • discovery: iCloud Drive, then ~/Library/CloudStorage/*, ~/Dropbox, ~/Google Drive, ~/OneDrive*, ~/Box, then ~/Documents. A Health Access folder holds an index.json of schema healthaccess.index.v1 and a workouts/ directory. If discovery finds several, the server refuses to guess and lists them.

Run healthfolder --check to print the resolved folder, where it came from, and the zone bounds in effect.

CLI

npx -y @reneturcios/healthfolder --check
npx -y @reneturcios/healthfolder list --sport swim --limit 5
npx -y @reneturcios/healthfolder show 7A1C3E90
npx -y @reneturcios/healthfolder vitals --days 14
npx -y @reneturcios/healthfolder summary --weeks 4
npx -y @reneturcios/healthfolder rate 7A1C3E90 7 "legs heavy, breathing fine"
npx -y @reneturcios/healthfolder plan push ./s30.json
npx -y @reneturcios/healthfolder plan status 2026-10-24T20:22:05Z-s30
npx -y @reneturcios/healthfolder plan list
npx -y @reneturcios/healthfolder day 2026-05-14
npx -y @reneturcios/healthfolder days --from 2026-05-01 --to 2026-05-14 --groups heart,sleep
npx -y @reneturcios/healthfolder catalog
npx -y @reneturcios/healthfolder profile
npx -y @reneturcios/healthfolder ecgs

Every command prints pretty JSON. Exit codes: 0 success, 1 domain or IO error, 2 usage error. --dir works on every subcommand.

MCP server

npx -y @reneturcios/healthfolder mcp

MCP client config:

{
  "mcpServers": {
    "healthfolder": {
      "command": "npx",
      "args": ["-y", "@reneturcios/healthfolder", "mcp"]
    }
  }
}

Add "--dir", "<absolute path>" to args to skip the folder search.

Tools (13)

ToolWhat it does
list_workoutsWorkouts from the index (deduplicated, newest first), one summary line each
get_workoutOne workout in full: document, summary, parts text, zones, cardiac cost
get_vitalsResting HR, HRV, body mass, sleep, steps, VO2max, blood pressure over the last N days
training_summaryOne entry per ISO week: volume, zones, effort, cardiac cost, rings from day files
rate_workoutStore the athlete's own effort rating (1–10) in the workout file
push_planValidate a training plan and write plans/<id>.json for the app to put on the Watch
get_plan_statusA plan's state: pending, scheduled, failed or cancelled
list_plansEvery plan with its state and planned distance/time
get_dayOne day file with every Health group it holds
get_daysDay files in a range (max 366 days), optionally reduced to named groups
get_catalogcatalog.json plus the export-coverage check
get_profileprofile.json as written
list_ecgsEvery ECG summary in ecg/, newest first

All results are pretty-printed JSON text with floats rounded to one decimal. Malformed arguments are JSON-RPC InvalidParams errors; domain problems (unknown id, failed validation, a refusal) come back as isError results with the reason in plain words.

Two safety rules are built in:

  • rate_workout only ever stores a number the athlete said. A workout that already carries Apple's own rating (effort_source: "apple") is refused unless the call passes override: true — the refusal names the file, the existing rating, and the remedy, and nothing is written.
  • push_plan reports every validation violation in one message (sport, name length, block/step shape, goal type and value, zone, label length), so a plan can be fixed in one pass.

Resources: healthaccess://index (the deduplicated index) and the template healthaccess://workout/{id} (one workout, heart-rate series included). MCP prompts are not implemented in this build.

Derived numbers

  • Zones. Four bounds, five zones. Bounds come from config.toml, else are estimated from the age in profile.json: max HR = 208 − 0.7 × age, bounds at 72/79/86/92 % of it (age 34 → [133, 146, 158, 169]). Zone seconds are computed from the heart-rate series: each sample's time until the next sample goes to that sample's zone, only time inside the workout counts, gaps over 60 s count as 60 s. With no bounds known, a file's own legacy zones are shown, marked bounds_source: "file".
  • Cardiac cost (swims only) = avg HR × best freestyle 100 m pace (s) / 60 = heart beats per 100 m; lower is better. The best 100 is the fastest work step of at least 95 m whose overlapping laps were at least half freestyle, normalised to 100 m; if no step qualifies and the swim is at least 100 m, the whole-workout pace is used.
  • Summary lines look like: 2026-05-14 Pool swim "S12" · 1200 m · 36:06 · avg HR 129 (max 162) · Z1–2 93% · effort 6/10 (apple) · best FR 100 2:12 → 284 beats/100 m — each segment appears only when its data exists.

Presence handshake

On initialize, and after tool calls at most once a minute, the server writes mcp/clients/<id>.json in the folder so the app can show "Connected". The id is the FNV-1a 64 hash of machine name + client name; the file carries the client name/version from the initialize request, the machine name (HEALTHACCESS_MACHINE overrides it), call counts carried over between writes, and the last tool name — never arguments or results. A failed write never fails a tool call. The CLI writes the same file, as client healthfolder-cli, only from the commands that write into the folder anyway (rate, plan push).

Docker

docker run -i ghcr.io/cloudcorprecords/healthfolder            # stdio MCP server
docker run --rm ghcr.io/cloudcorprecords/healthfolder list --dir /data

Mount the Health Access folder into the container and pass --dir (or set HEALTHACCESS_DIR) so the server can find it.

Development

npm install
npm run build   # tsc -> dist/
npm test        # tsx --test test/*.test.ts (synthetic folders, no network)
npm run dev     # run the CLI from source

License

MIT — Copyright (c) 2026 Rene Turcios.

FAQs

Package last updated on 05 Nov 2026

Related posts