Local-first personal health record: labs, meds, wearables, food log, deterministic safety checks
Runs on your machine from its package; the steps are below.
At most: creates or changes data: records, files, settings.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
docker run -i --rm ghcr.io/andronaft/health-os:0.2.1
MCP client config
{
"mcpServers": {
"health-os": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/andronaft/health-os:0.2.1"
]
}
}
}Local-first personal health record, exposed over MCP. Your labs, diagnoses, medications, wearable data and food log live in your own Postgres; any MCP client — one running a local model or a cloud assistant — can read and update them through guarded tools. Critical values, drug-safety rules and screening schedules are deterministic code, not LLM judgement.
Medical disclaimer. This is not a medical device and does not give medical advice. Critical-value alerts and screening reminders are only a signal to contact a doctor — never a diagnosis and never a reason to delay care. Use at your own risk.
Only Docker needed. Starts a throwaway database with a fictional patient — two years of labs (LDL creeping up), blood pressure, medications, a food log and a lab panel awaiting approval:
git clone https://github.com/andronaft/health-os && cd health-os/demo
docker compose up -d --build
Point your MCP client at it:
{
"mcpServers": {
"health-os-demo": {
"command": "docker",
"args": ["run", "-i", "--rm", "--network", "health-os-demo",
"-e", "DATABASE_URL=postgresql+psycopg://health:demo@db:5432/health_os",
"health-os:local"]
}
}
}
Ask "show my health summary", "is my LDL trending up?", "what's pending review?",
"what am I short on nutritionally?". Remove it all with docker compose down -v.
Requires Docker and Python 3.12+.
git clone https://github.com/andronaft/health-os && cd health-os
cp .env.example .env # set the passwords
docker compose up -d db # Postgres 16 + pgvector
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/alembic upgrade head # schema
.venv/bin/python -m seed.load # marker catalog, synonyms, units, nutrients
.venv/bin/python -m seed.demo # optional: a fictional demo patient to play with
Then connect an MCP client — config for LM Studio, Open WebUI, Ollama CLI and Claude is in mcp_server/README.md. Try: "show my health summary", "LDL trend", "what am I short on nutritionally this week?".
Local models: the server speaks standard MCP over stdio, so any MCP client that runs a local model can use it. Verified so far: the server itself with the official MCP Python client (CI + the Docker demo). Not yet verified end-to-end with a local model — see #9; reports welcome.
Every row of a staged panel ends up pending — nothing becomes a fact without an explicit approve. A row the catalog can't place is kept, not dropped:
Unmapped and pending rows show up in list_pending_reviews and in the health summary's
"awaiting review" block; a learned name maps automatically on the next panel.
| Directory | What's inside |
|---|---|
core/ | config, DB, normalization, services, dedup, health summary |
mcp_server/ | MCP server, read and write tools |
safety/ | deterministic safety rules and alert delivery |
analytics/ | trends, baselines, calculators, screening, nutrition, reports |
ingestion/ | extraction schema, confidence scoring, device importers, embeddings |
migrations/ | Alembic schema |
seed/ | reference catalog + the demo patient |
evals/ | red-team scenarios (injections, hidden critical values, unit tricks) |
scripts/ | backup/restore (restic; install_launchd.sh schedules them on macOS), read-only role setup, importers |
data/ and .env
are outside git. Nothing is sent anywhere by health-os itself.make test # everything (needs Postgres for the integration part)
make test-unit # pure unit tests — no database needed
make test-integration # only tests marked `integration`
Integration tests never touch the working database: tests/conftest.py drops and recreates
<POSTGRES_DB>_test on the same server (migrations + seed) on every run. Override with
TEST_DATABASE_URL (the name must end in _test). Without Postgres, integration tests are
skipped locally; CI sets REQUIRE_DB=1 so they fail instead.
Development history: PROGRESS.md.
AGPL-3.0-or-later. You may use, modify and fork health-os; if you distribute it or run a modified version as a network service, you must publish your source under the same license.
Want to use it in a closed-source or commercial product without those obligations? A separate commercial license is available from the author — reach out via GitHub (@andronaft).
Contributions are welcome — see CONTRIBUTING.md (includes a short CLA).