vitals
README.md
<p align="center">
<img src="assets/VitalsIcon.icon/Assets/vitals-trazos-1000.png" alt="Vitals logo" width="150">
</p>
# Vitals — self-hosted WHOOP / Google Health alternative
[](https://github.com/DocStream-Oficial/vitals/actions/workflows/ci.yml)
[](LICENSE)
[](https://www.python.org/)
[](#tests)
**Your data. Your server. Your AI.**
A personal recovery / strain / sleep / body-age dashboard — the kind of thing you'd
pay a monthly subscription for — except it runs on hardware you own, reads from
wearables you already have (Google Health, Oura, WHOOP, Apple HealthKit), and never
sends a byte of your health data to a third-party cloud.
<p align="center">
<img src="assets/demo/vitals-today-desktop.png" alt="Vitals — Today tab (desktop)" width="68%">
<img src="assets/demo/vitals-today-mobile.png" alt="Vitals — Today tab (mobile PWA)" width="26%">
</p>
<p align="center">
<img src="assets/demo/vitals-sleep-mobile.png" alt="Sleep — hypnogram, stages, efficiency" width="31%">
<img src="assets/demo/vitals-trends-mobile.png" alt="Trends — week vs week, healthspan pace" width="31%">
<img src="assets/demo/vitals-coach-mobile.png" alt="AI Coach — conversational, runs on your hardware" width="31%">
</p>
<p align="center"><sub>Sleep stages & hypnogram · week-over-week trends & healthspan pace · conversational AI coach.<br>All screenshots from demo mode (<code>VITALS_DEMO=1</code>) — 100% synthetic data, no real user involved.</sub></p>
---
## Agent-first install
**Already have an AI agent and a machine that's always on?** Don't install anything
by hand. Paste the prompt below to **Claude Code, OpenClaw, Hermes, or any agent
that can run shell commands** — it clones the repo, installs and launches Vitals,
and wires *itself* in over MCP so it can read your recovery, sleep, and HRV. From
zero to a working, agent-connected health server in one message:
```text
Set up Vitals for me end-to-end on this machine, then connect yourself to it over MCP.
Vitals is a self-hosted health dashboard: https://github.com/DocStream-Oficial/vitals
1. Clone it and remember the absolute path as REPO:
git clone https://github.com/DocStream-Oficial/vitals.git
2. Install it (needs Python 3.10+). Run the non-interactive setup so it doesn't block:
cd REPO && python install.py --no-launch --yes
Then start the app in the background using the venv install.py just created
(it prints the exact uvicorn command; venv is .venv/bin on macOS/Linux,
.venv\Scripts on Windows). Confirm the dashboard responds at http://localhost:8700.
To preview it before I add real credentials, launch with demo data instead:
python install.py --demo --yes
3. Wire up the MCP server so YOU can query my data:
pip install -r REPO/requirements-mcp.txt
Register REPO/vitals_mcp.py as a stdio MCP server named "vitals" in your MCP
config, then reload your MCP connections.
4. Verify: confirm the 9 vitals_* tools loaded, call vitals_today, and give me
today's recovery, sleep and strain in one line.
5. Then tell me the one manual step left: which OAuth credentials to add to
REPO/.env to connect my real wearables (Google / Oura / WHOOP) and where to
get them — follow the repo README.
6. Ongoing: send me vitals_morning_brief each morning and vitals_bedtime_brief at
night, and alert me proactively whenever recovery drops below 40% or an
illness-risk insight appears in vitals_insights.
```
That's the whole point of Vitals being MCP-native: your agent doesn't just *use*
it, it *installs* it. No agent yet? Jump to [Quick start](#quick-start) for the
one-command manual install.
---
## Why Vitals
Wearable dashboards (WHOOP, Oura, Fitbit Premium) are subscription products: your
recovery, sleep, and HRV history live on someone else's server, gated behind a
monthly fee, and the scoring logic is a black box.
Vitals takes the opposite bet:
- **Self-hosted, always** — Docker Compose or a plain Python venv on a box you
control (a spare Mac mini, a home server, a $5 VPS). No subscription, ever.
- **Bring your own OAuth** — you create your own Google/Oura/WHOOP developer
credentials; Vitals never proxies your data through a shared backend.
- **Transparent scoring** — recovery, strain, sleep performance, and body-age are
plain Python you can read in an afternoon. See [`docs/ALGORITHMS.md`](docs/ALGORITHMS.md)
for the exact formulas, weights, and — just as important — their honest limitations.
- **AI-native, via MCP** — `vitals_mcp.py` exposes your health data as 9 MCP
tools, so a local AI agent ([OpenClaw](https://openclaw.io),
[Hermes](https://hermes-agent.nousresearch.com), Claude, or anything speaking
MCP) can reason over your recovery trend, flag an early illness signal, or
answer "should I train hard today?" without any of that data leaving your
network. Your agent can even [install the whole thing itself](#agent-first-install)
from a single copy-paste prompt.
- **Multi-source, source-agnostic** — connect Google Health, Oura, WHOOP, or
Apple HealthKit (or several at once); the scoring engine normalizes whatever
comes in to one internal schema.
- **One install, your whole family** — Vitals is multi-profile out of the box.
Each person (partner, kids, parents) gets their own fully isolated data,
scores, and coach. WHOOP and Oura charge *per person* — a family of 5 is
~$150/month. One $5 VPS running Vitals is **~$1/person/month**, and it's
the same math whether you're 2 people or 8.
If you've ever wanted your quantified-self data to actually be *yours* — inspectable,
exportable, and running on infrastructure you control — this is that.
---
## Features
| Area | What it does |
|---|---|
| **Today** | Recovery ring (HRV/RHR/sleep composite), strain, sleep breakdown, HRV/RHR vs. rolling baseline |
| **Trends** | 7d/30d rolling views of recovery, HRV, sleep, training load (ACWR) |
| **Coach** | Conversational AI coach (multi-conversation, ChatGPT/Claude-style) via your local `claude` CLI — no API key needed if you already run OpenClaw |
| **Journal + Behavior Impact** | Daily habit tracker (~33 habits: supplements, alcohol, meditation, screens-in-bed, etc.) + a statistical engine that finds which habits *actually* move your recovery/HRV/sleep — Spearman correlation with Benjamini-Hochberg correction, not vibes |
| **Narrative reports** | Weekly/monthly plain-language summaries of what changed and why |
| **Labs** | Manual blood-test tracking (~20 common biomarkers) with reference ranges and out-of-range flags |
| **Body-age & Healthspan** | VO2max-based fitness age (NTNU/Nes 2011 formula) plus a monthly body-age-vs-chronological-age trend |
| **Household / multi-profile** | One install serves your whole family — each person (partner, kids, parents) gets fully isolated data, scores & coach. Split a $5 VPS 5 ways = ~$1/person/month vs. ~$30/person on WHOOP or Oura |
| **Push notifications** | ntfy/Telegram alerts for recovery drops, illness-risk signals, sleep debt |
| **Offline-capable PWA** | Add to Home Screen on iOS/Android; service worker caches the shell |
| **Native iOS app (BYO dev account)** | Capacitor shell with a native HealthKit plugin: Apple Watch HRV, sleep stages, VO₂max, ECG — built and signed by you, distributed via TestFlight |
| **AI agent integration (MCP)** | 9 read-only MCP tools for OpenClaw / Hermes / Claude — morning briefs, bedtime reminders, proactive alerts from your own agent |
| **Female health module** | Opt-in cycle tracking (phase, fertile window, delay detection, peri/menopause signals) — zero data exposure unless explicitly enabled |
| **ECG viewer** | Isolated viewer for Apple Watch ECG waveforms pushed from the iOS companion app |
| **Data export** | Full JSON or flattened CSV export of your own history, any time |
---
## Supported data sources
| Source | Auth | Notes |
|---|---|---|
| **Google Health** | OAuth 2.0 (BYO client) | The new Google Health API (`health.googleapis.com`) — Fitbit, Pixel Watch, Garmin, and anything that syncs into Health Connect |
| **Oura** | OAuth 2.0 (BYO client) | Oura Ring v2 API |
| **WHOOP** | OAuth 2.0 (BYO client) | WHOOP v2 API — requires `offline` scope for refresh tokens |
| **Apple HealthKit** | Push token (`INGEST_TOKEN`) | Companion iOS app reads HealthKit on-device and pushes to `/api/ingest` — no cloud round-trip |
You can connect more than one source at once; the sync engine merges them into a
single daily record per metric.
---
## Quick start
### Docker (recommended)
No Python setup, no venv — just Docker.
```bash
git clone https://github.com/DocStream-Oficial/vitals.git
cd vitals
cp .env.example .env # fill in your credentials (see below), or skip for demo mode
docker compose up -d
# open http://localhost:8700
```
Want to try it before connecting any real credentials? Run it in **demo mode**
instead — a deterministic synthetic dataset (150 days of realistic
recovery/sleep/HRV/strain, plus sample journal entries and lab results) with
no OAuth, no `.env`, no wearable required:
```bash
docker compose run --rm -e VITALS_DEMO=1 -p 8700:8700 vitals
# open http://localhost:8700 — fully populated dashboard, no login
```
In demo mode, every write-y endpoint (sync, OAuth login, source connect/disconnect,
HealthKit ingest) responds `200 {"status": "demo"}` instead of touching real
credentials or `data/`. Journal/labs entries you add in demo mode go to a
throwaway temp directory that's discarded when the process exits — nothing you
do in demo mode can affect a real install running alongside it.
### No Docker — one command (`install.py`)
Prefer a plain Python venv over Docker? `install.py` is a single cross-platform
script (Windows/Mac/Linux, stdlib only) that creates the venv, installs
dependencies, generates `.env`, and launches the app — no manual steps.
```bash
git clone https://github.com/DocStream-Oficial/vitals.git
cd vitals
python install.py
# open http://localhost:8700
```
It's idempotent — re-run it any time; it reuses an existing `.venv` and never
overwrites an existing `.env`. Try demo mode the same way, with zero credentials:
```bash
python install.py --demo
```
#### Flags
| Flag | What it does |
|---|---|
| *(none)* | Interactive wizard (all fields optional, Enter = default/skip), then launches the app |
| `--demo` | Sets `VITALS_DEMO=1`, skips all credential prompts, launches straight into the demo dataset |
| `--no-launch` | Only does setup (venv + deps + `.env`); prints how to start the app manually, doesn't launch it |
| `-y`, `--yes` | Non-interactive: copies `.env.example` as-is (auto-generating `INGEST_TOKEN`), no prompts — for CI/scripts |
| `--port N` | Port for uvicorn (default `8700`) |
| `--help` | Full usage |
`install.py` requires Python 3.9+ (same minimum as the rest of the project) and
uses whichever interpreter you invoked it with (`python`, `python3`, or `py -3`
on Windows) to create the venv — no need to guess the right command for your
platform.
### Manual setup (venv, no installer)
If you'd rather run the steps yourself:
```bash
git clone https://github.com/DocStream-Oficial/vitals.git
cd vitals
python3 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # fill in your credentials (see below)
uvicorn main:app --host 127.0.0.1 --port 8700 --reload
# open http://localhost:8700
```
### Windows service (NSSM)
For a persistent background service (not just a dev session), see
`deploy_windows.ps1` for a step-by-step PowerShell script.
Requires Python 3.9+, NSSM, and Tailscale (for remote HTTPS access).
---
## Connecting your wearables (Google Health, Oura, WHOOP)
Vitals never uses a shared account — **each person creates their own free OAuth
app** on the provider's site and drops two values (`CLIENT_ID` / `CLIENT_SECRET`)
into `.env`. It's the same three-step pattern everywhere: create the app, trust
`http://localhost:8700/auth/callback`, copy the two keys.
📖 **Full step-by-step walkthrough (beginner-friendly, no experience needed):
[`docs/CONNECT-DATA-SOURCES.md`](docs/CONNECT-DATA-SOURCES.md)** — click-by-click
for Google Health, Oura, and WHOOP, with the exact scopes and redirect URIs, plus
troubleshooting.
Quick reference:
| Provider | Developer portal | `.env` keys |
|---|---|---|
| **Google Health** | [console.cloud.google.com](https://console.cloud.google.com/) → enable the **Google Health API** | `CLIENT_ID`, `CLIENT_SECRET` |
| **Oura** | [cloud.ouraring.com/oauth/applications](https://cloud.ouraring.com/oauth/applications) | `OURA_CLIENT_ID`, `OURA_CLIENT_SECRET` |
| **WHOOP** | [developer.whoop.com](https://developer.whoop.com) (needs the `offline` scope) | `WHOOP_CLIENT_ID`, `WHOOP_CLIENT_SECRET` |
> Vitals uses Google's current **Google Health API** (`health.googleapis.com`) —
> not the legacy Google Fit / Fitness API that Google is retiring at the end of 2026.
Apple HealthKit doesn't use OAuth; the native iOS app pushes data on-device — see
[`docs/IOS-HEALTHKIT.md`](docs/IOS-HEALTHKIT.md).
---
## Configuration (`.env`)
```bash
cp .env.example .env
```
See [`.env.example`](.env.example) for the full list of variables with inline
comments (OAuth credentials per source, profile defaults, `INGEST_TOKEN`,
notification settings, Claude CLI path, and `VITALS_DEMO`).
### Coach with a local LLM (Ollama, LM Studio, llama.cpp)
By default the Coach (chat, headline, and narrative reports) shells out to
your local `claude` CLI — no API key needed. If you'd rather run everything
fully offline against a self-hosted model, set `COACH_BACKEND=openai_compat`
in `.env` and point it at any OpenAI-compatible `/chat/completions` endpoint:
```bash
# .env
COACH_BACKEND=openai_compat
COACH_API_BASE=http://localhost:11434/v1 # Ollama default
COACH_MODEL=llama3.1 # must already be pulled/loaded
COACH_API_KEY= # leave empty for local servers
```
This works with [Ollama](https://ollama.com) (`ollama pull llama3.1` first),
[LM Studio](https://lmstudio.ai)'s local server, or any `llama.cpp`-based
server that speaks the OpenAI chat-completions format. The request is a
single stdlib `urllib` POST (no new pip dependency, no SDK) — same
best-effort semantics as the CLI: a slow/unreachable server times out and
falls back gracefully, it never blocks a request or crashes the app.
`COACH_BACKEND` unset (or any unrecognized value) keeps the default
`claude_cli` behavior — nothing changes if you don't touch this.
### Dashboard login (`DASHBOARD_TOKEN`)
By default, anyone who can reach the app's port can view your dashboard — no
login screen. Set `DASHBOARD_TOKEN` in `.env` to require authentication:
```bash
# .env
DASHBOARD_TOKEN=$(python3 -c "import secrets; print(secrets.token_urlsafe(32))")
```
Then log in once at `/login` (sets a long-lived cookie), or send
`Authorization: Bearer <DASHBOARD_TOKEN>` on every request (handy for `curl`
or Grafana). `/api/ingest` + `/api/ecg` (the iOS app's own push endpoints) and
`/api/v1/*` (the public read-only API) keep their own independent auth either
way — this only gates the dashboard itself. Full details, including the CSRF
side-benefit, in `SECURITY.md`.
---
## Architecture / Tech stack
- **Backend**: FastAPI (Python 3.9+), vanilla JS frontend (no build step), served
as a single process — `uvicorn main:app`.
- **Persistence**: flat JSON files under `data/` (no database). Atomic writes
(`.tmp` + `os.replace`) everywhere; each domain (profile, journal, labs, cycle,
coach history) has its own file. Household/multi-profile mode namespaces these
under `data/users/<uid>/`.
See [`docs/ALGORITHMS.md`](docs/ALGORITHMS.md) for how the raw per-source
payloads get normalized into the daily schema.
- **Scheduler**: APScheduler background job for the daily sync (`SYNC_HOUR` in
`.env`), plus a best-effort sync on boot.
See [`docs/ALGORITHMS.md`](docs/ALGORITHMS.md) for the scoring pipeline that
runs on every sync (recovery, strain, sleep performance, body-age).
- **AI coach**: shells out to your local `claude` CLI (no API key required if
you already run [OpenClaw](https://openclaw.io)); degrades gracefully to a
deterministic fallback narrative if the CLI isn't available.
- **MCP server**: `vitals_mcp.py` (separate process, Python 3.10+/`mcp` SDK)
exposes your data as MCP tools for any MCP-speaking agent.
- **Mobile**: Capacitor-wrapped iOS app (`ios/`) for native HealthKit/ECG access,
talking to the same FastAPI backend over `/api/ingest`.
- **Interactive API docs**: FastAPI auto-generates OpenAPI docs at
[`/docs`](http://localhost:8700/docs) once the server is running — every
endpoint, request/response schema, and a "Try it out" console, for free.
---
## Endpoints
Full interactive reference at `/docs` once running. Complete list of all
routes in `main.py`, with their auth model:
- **none** — no auth of its own; open by design (login page, static PWA
assets) or intentionally public.
- **dashboard** — protected by `DASHBOARD_TOKEN` when you set one (cookie or
`Authorization: Bearer`); wide open if you leave it empty (default). See
`SECURITY.md`.
- **ingest-token** — requires the `X-Vitals-Token` header (`INGEST_TOKEN`),
independent of `DASHBOARD_TOKEN`.
- **api-key** — requires `Authorization: Bearer vk_...` (a key you generate
under More → API), independent of `DASHBOARD_TOKEN`.
| Method | Path | Description | Auth |
|--------|------|--------------|------|
| GET | `/` | Dashboard HTML | dashboard |
| GET | `/manifest.webmanifest` | PWA manifest | none |
| GET | `/service-worker.js` | PWA service worker | none |
| GET | `/api/ingest-token` | Shows the current `INGEST_TOKEN` (for pairing) | dashboard |
| GET | `/api/qr` | SVG QR code for pairing (embeds the token in the URL) | dashboard |
| GET | `/login` | Dashboard login form | none |
| POST | `/login` | Validate `DASHBOARD_TOKEN`, set session cookie | none |
| GET | `/api/insights` | Rule-based insight cards | dashboard |
| GET | `/api/coach/suggestions` | Suggested Coach questions | dashboard |
| GET | `/api/drivers` | Statistical drivers of recovery | dashboard |
| GET | `/api/report` | Weekly/monthly narrative report | dashboard |
| GET | `/api/data` | JSON of your health data | dashboard |
| GET | `/api/export` | Export full dataset (`?fmt=json\|csv`) | dashboard |
| POST | `/api/sync` | Trigger a manual sync (no-op in demo mode) | dashboard |
| POST | `/api/ingest` | HealthKit push ingestion (iOS app) | ingest-token |
| POST | `/api/ecg` | ECG reading push ingestion (iOS app) | ingest-token |
| GET | `/api/ecg` | List ECG readings (no voltages) | dashboard |
| GET | `/api/ecg/{uuid}` | Full ECG reading incl. voltages | dashboard |
| GET | `/auth/login` | Start OAuth flow (disabled in demo mode) | dashboard |
| GET | `/api/profile` | Read profile | dashboard |
| PUT | `/api/profile` | Update profile | dashboard |
| POST | `/api/sources/{name}` | Connect a data source | dashboard |
| DELETE | `/api/sources/{name}` | Disconnect a data source | dashboard |
| GET | `/api/sources` | List connected sources | dashboard |
| GET | `/api/cycle` | Cycle tracking state | dashboard |
| POST | `/api/cycle/period` | Log a period | dashboard |
| DELETE | `/api/cycle/period/{start}` | Delete a logged period | dashboard |
| POST | `/api/cycle/symptom` | Log a cycle symptom | dashboard |
| GET | `/api/journal` | Habit journal entries | dashboard |
| PUT | `/api/journal/{date}` | Update a journal entry | dashboard |
| POST | `/api/journal/custom` | Add a custom journal habit | dashboard |
| GET | `/api/journal/impact` | Behavior Impact engine findings | dashboard |
| GET | `/api/journal/dose-response` | Dose-response curve for a habit | dashboard |
| GET | `/api/sleep-coach` | Sleep coach card | dashboard |
| GET | `/api/programs` | Coach programs | dashboard |
| GET | `/api/plan` | Active plan card | dashboard |
| POST | `/api/plan` | Start/update a plan | dashboard |
| DELETE | `/api/plan` | Clear the active plan | dashboard |
| POST | `/api/plan/check` | Check off a plan step | dashboard |
| GET | `/api/labs` | Blood-test entries | dashboard |
| POST | `/api/labs` | Add a blood-test entry | dashboard |
| POST | `/api/labs/import` | Import blood tests from CSV | dashboard |
| DELETE | `/api/labs/{entry_id}` | Delete a blood-test entry | dashboard |
| GET | `/api/healthspan` | Body-age-vs-chronological-age trend | dashboard |
| GET | `/api/coach/conversations` | List Coach conversations | dashboard |
| POST | `/api/coach/conversations` | Start a new Coach conversation | dashboard |
| GET | `/api/coach/conversations/{cid}` | Read a Coach conversation | dashboard |
| DELETE | `/api/coach/conversations/{cid}` | Delete a Coach conversation | dashboard |
| POST | `/api/coach` | Ask the Coach (chat) | dashboard |
| GET | `/api/coach/history` | Legacy Coach history | dashboard |
| DELETE | `/api/coach/history` | Clear legacy Coach history | dashboard |
| GET | `/api/users` | List household users | dashboard |
| POST | `/api/users` | Create a household user | dashboard |
| DELETE | `/api/users/{uid}` | Delete a household user | dashboard |
| POST | `/api/keys` | Create a public read-only API key | dashboard |
| GET | `/api/keys` | List your API keys (metadata only) | dashboard |
| DELETE | `/api/keys/{key_id}` | Revoke an API key | dashboard |
| GET | `/api/v1/data` | Public read-only API: full dataset | api-key |
| GET | `/api/v1/insights` | Public read-only API: insight cards | api-key |
| GET | `/auth/callback` | OAuth callback — saves your token | dashboard |
---
## Public read-only API
Build on top of your own data (same pattern as [wger](https://wger.de)/Open
Wearables) without exposing `INGEST_TOKEN` (the HealthKit/ECG write secret) to
third-party integrations. From **More → API**, generate a `vk_...` key — it is
shown **only once**; save it, it cannot be recovered later.
```bash
curl -H "Authorization: Bearer vk_..." https://your-instance.example/api/v1/data
curl -H "Authorization: Bearer vk_..." https://your-instance.example/api/v1/insights
```
- Per-user, revocable, **read-only** keys — there is no write endpoint under
`/api/v1/*`.
- Only the SHA-256 hash of the key is persisted; the raw key is never stored
and cannot be recovered after creation.
- Missing, invalid, or revoked key → `401` (never `500`).
- Up to 10 active keys per user; revoke them from the same section.
---
## Onboarding & profile
On first launch, Vitals walks you through a short onboarding flow where you set your
language, units (metric / imperial), and profile. You can update these later under
the **More** tab → Profile.
---
## Native iOS app (Apple Watch / HealthKit) — bring your own dev account
The repo ships a full native iOS companion app (`ios/`, Capacitor + WKWebView):
a thin shell that loads **your** self-hosted instance and adds what a PWA can't —
**native HealthKit access**:
- Reads ~13 HealthKit metrics, including Apple Watch data: HRV, resting HR,
respiratory rate, SpO₂, wrist temperature, VO₂max, sleep stages, workouts,
steps, active energy.
- Pushes them to your instance (`POST /api/ingest`, authenticated with
`X-Vitals-Token`) and auto-syncs every time the app comes to the foreground.
- Apple Watch **ECG waveforms** push too — viewable in the dashboard's ECG viewer.
You build it yourself with your own Apple account — no binaries distributed,
no App Store middleman for your health data:
- **Free Apple ID** → run it on your own iPhone (7-day signing).
- **Paid Apple Developer account** ($99/yr) → distribute to yourself and family
via **TestFlight** (~30 min setup).
Step-by-step guides: [docs/IOS-BETA.md](docs/IOS-BETA.md) (build + TestFlight)
and [docs/IOS-HEALTHKIT.md](docs/IOS-HEALTHKIT.md) (HealthKit wiring in Xcode).
---
## PWA — Add to Home Screen
Open `http://localhost:8700` (or your HTTPS URL) in Safari or Chrome.
Tap **Share → Add to Home Screen** (iOS) or the install prompt (Android/Chrome).
Vitals runs as a full-screen, offline-capable app with no browser chrome.
---
## Connect your AI agent (OpenClaw / Hermes / any MCP client)
This is where Vitals stops being a dashboard and becomes infrastructure: if you
run [OpenClaw](https://openclaw.io), [Hermes Agent](https://hermes-agent.nousresearch.com),
Claude, or any other MCP-speaking agent, `vitals_mcp.py` turns your health data
into **9 read-only tools** your agent can call — locally, with nothing sent to
a third party. Your agent can answer *"should I train hard today?"* from your
actual HRV, message you a morning brief over WhatsApp/Telegram, or flag an
illness-risk signal before you feel it.
```bash
# Install extra deps (Python 3.10+ required for the mcp SDK)
pip install -r requirements-mcp.txt
# Run the MCP server (stdio transport — your agent will spawn it)
python3 vitals_mcp.py
```
Available tools: `vitals_today`, `vitals_trends`, `vitals_insights`, `vitals_bodyage`,
`vitals_morning_brief`, `vitals_bedtime_brief`, `vitals_drivers`, `vitals_cycle_summary`,
`vitals_ask_coach`.
### Already running Vitals? Just add the MCP
If you installed manually and Vitals is already up, this shorter prompt wires only
the MCP layer (the [Agent-first install](#agent-first-install) prompt at the top
does the full clone-install-connect flow from scratch):
```text
I run a self-hosted Vitals instance (https://github.com/DocStream-Oficial/vitals).
The repo is cloned at <PATH-TO-REPO> and the app runs at <MY-VITALS-URL>.
Set up its MCP server so you can read my health data:
1. Install the MCP extras: pip install -r <PATH-TO-REPO>/requirements-mcp.txt
(needs Python 3.10+).
2. Register <PATH-TO-REPO>/vitals_mcp.py as a stdio MCP server named "vitals"
in your MCP config, then reload your MCP connections.
3. Verify the 9 vitals_* tools are available, call vitals_today, and give me
today's recovery, sleep and strain in one line.
4. From now on: include vitals_morning_brief in my morning routine, send me
vitals_bedtime_brief at night, and alert me proactively whenever recovery
drops below 40% or an illness-risk insight appears in vitals_insights.
```
That last step is the fun part — a scheduled agent + these tools = a proactive
health copilot that costs you $0/month in subscriptions.
---
## Tests
```bash
source .venv/bin/activate
pip install -r requirements-dev.txt
pytest -q
```
1,600+ tests, all against **synthetic fixtures** — no real health data is required
or shipped in the repo. CI runs the full suite on Python 3.9 and 3.12 on every
push/PR (see [`.github/workflows/ci.yml`](.github/workflows/ci.yml)).
### iOS sleep aggregator
The Xcode project has no test target, so the sleep-night reconstruction logic
lives in `ios/App/App/SleepAggregator.swift` as a **Foundation-only** file (no
`import HealthKit`) precisely so it can be compiled and exercised on a Mac:
```bash
swiftc -o /tmp/verify_sleep ios/App/App/SleepAggregator.swift scripts/verify_sleep_union.swift && /tmp/verify_sleep
```
17 cases covering interval union, per-source night reconstruction and the
hypnogram contract. Run it after touching `SleepAggregator.swift` — it is the
only automated check that code has.
```bash
python scripts/i18n_audit.py # verifies all 4 locales (ES/EN/FR/PT) are in sync
```
---
## Security & publishing your own fork
See [`SECURITY.md`](SECURITY.md) for how to report a vulnerability, and
[`CONTRIBUTING.md`](CONTRIBUTING.md#before-you-publish) for the pre-publish
checklist (never commit `.env` or `data/`, rotate credentials before making a
fork public, etc.). `scripts/preflight_publish.sh` automates that check.
---
## License
MIT © 2026 DocStream. See [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues