Health Vault
Self-hosted MCP server that syncs Xiaomi fitness data to SQLite and provides authenticated tools to query health metrics (steps, sleep, HR, etc.) for AI assistants like Grok.
README
Health Vault
Self-hosted Xiaomi Fitness / Smart Band → SQLite → authenticated MCP for Grok (CLI and grok.com Custom Connector).
Mi Band → Mi Fitness (phone) → Xiaomi cloud
↓
health sync (this project)
↓
SQLite
↓
MCP tools (OAuth or API key)
↓
Grok answers with real data
Not affiliated with Xiaomi or xAI. Uses an unofficial reverse-engineered Health API. Personal / research use. Wearable data is wellness only, not medical advice.
Requirements
| Need | Notes |
|---|---|
| Python 3.12+ | Required (requires-python >= 3.12) |
| Xiaomi Smart Band + Mi Fitness app | Band must sync to the phone app first |
| Xiaomi account | QR login from this tool |
| Optional: cloudflared or ngrok | For grok.com (public HTTPS) |
| Optional: Grok CLI / Grok Build | Local MCP with Bearer API key |
Quick install
git clone <this-repo> health-vault && cd health-vault
# Option A: uv
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -e .
# Option B: plain venv
python3.12 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -U pip
pip install -e .
CLI entrypoint: .venv/bin/health (or health after activate).
Runtime data goes to ./data/ (gitignored): Xiaomi token, SQLite DB, OAuth state.
Scenario A — Local data + Grok CLI
Best for development and private use on one machine.
1. Pull data from Xiaomi
- On the phone, open Mi Fitness and sync the band.
- On the computer:
.venv/bin/health login # QR → scan with Xiaomi Account app
# QR is also saved as data/qr_login.png if you cannot see the terminal
.venv/bin/health sync --days 7
.venv/bin/health status # expect sync_health: ok
.venv/bin/health show --days 14
2. API key + MCP server
.venv/bin/health key create --name grok # prints Bearer token ONCE — save it
.venv/bin/health serve --host 127.0.0.1 --port 8787
3. Point Grok CLI / Grok Build at the server
~/.grok/config.toml (or your agent’s MCP config):
[mcp_servers.health]
url = "http://127.0.0.1:8787/mcp"
headers = { Authorization = "Bearer hlt_PASTE_YOUR_TOKEN" }
Restart the CLI/agent so it reloads MCP. Ask something concrete, e.g. “How did I sleep in the last 7 days?”
Smoke checks (A)
curl -sS http://127.0.0.1:8787/healthz
# → ok
curl -sS -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:8787/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json' -d '{}'
# → 401 (auth required)
.venv/bin/health tool health_status
.venv/bin/health tool health_context --hint sleep --days 7
Scenario B — grok.com Custom Connector (tunnel)
Grok’s cloud must call a public HTTPS MCP URL. Easiest path: local server + Cloudflare quick tunnel or ngrok. See also xAI tunneling notes.
1. Data + owner password
.venv/bin/health login
.venv/bin/health sync --days 7
.venv/bin/health oauth set-password # password for the browser login page
2. Serve with a public URL
One-shot (integrated tunnel):
# brew install cloudflared # if needed
.venv/bin/health serve --host 127.0.0.1 --port 8787 --tunnel
Copy the printed URL, e.g. https://xxxx.trycloudflare.com.
More reliable (two terminals):
# Terminal 1
.venv/bin/health serve --host 127.0.0.1 --port 8787 --public-url https://PLACEHOLDER
# Terminal 2 — after Uvicorn is up
cloudflared tunnel --url http://127.0.0.1:8787
# copy https://xxxx.trycloudflare.com
# Restart terminal 1 with the real public URL (OAuth issuer must match HTTPS host):
.venv/bin/health serve --host 127.0.0.1 --port 8787 \
--public-url https://xxxx.trycloudflare.com
Free tunnel URLs often change on restart — update the Grok connector when they do.
If the public URL returns 530 / Error 1033, wait a few seconds or use the two-terminal flow.
3. Connect in Grok
- Open grok.com/connectors → New Connector → Custom.
- Server URL:
https://xxxx.trycloudflare.com/mcp(must include/mcp). - If Grok shows an OAuth form: use PKCE; authorization/token endpoints from
https://xxxx.trycloudflare.com/.well-known/oauth-authorization-server;
scopehealth:read(Client Secret usually empty for public + PKCE). - Browser opens Health Vault login → enter the password from
health oauth set-password. - Chat with a real question (not only “list tools”).
More detail: docs/GROK_CONNECT.md.
Smoke checks (B)
curl -sS https://xxxx.trycloudflare.com/healthz
curl -sS https://xxxx.trycloudflare.com/.well-known/oauth-authorization-server | head
curl -sS -o /dev/null -w '%{http_code}\n' -X POST https://xxxx.trycloudflare.com/mcp \
-H 'Content-Type: application/json' -d '{}'
# → 401 without token
Scenario C — VPS / home server (generic production)
Pattern: app listens on HTTP on a private port; TLS and domain are terminated by a reverse proxy (Caddy, nginx, Traefik, etc.) in front. Do not put secrets in git.
Example names below are placeholders — choose your own domain and port.
1. Install on the host
sudo mkdir -p /opt/health-vault/data
sudo chown "$USER:$USER" /opt/health-vault /opt/health-vault/data
chmod 700 /opt/health-vault/data
# copy or git clone the project into /opt/health-vault
cd /opt/health-vault
python3.12 -m venv .venv
.venv/bin/pip install -U pip
.venv/bin/pip install -e .
2. Environment file (secrets, not in git)
sudo tee /etc/health-vault.env >/dev/null <<'EOF'
HEALTH_ROOT=/opt/health-vault
HEALTH_DATA_DIR=/opt/health-vault/data
PUBLIC_BASE_URL=https://health.example.com
HOST=0.0.0.0
PORT=8002
# Optional: bootstrap owner password once if DB has none yet
OAUTH_LOGIN_PASSWORD=change-me-to-a-long-random-string
EOF
sudo chmod 640 /etc/health-vault.env
# owner should be root + the service user (e.g. root:deploy)
PUBLIC_BASE_URL must be the public HTTPS origin users and Grok hit (no path).
3. systemd unit
Example unit (also see deploy/health-mcp.service as a template — rename paths):
[Unit]
Description=Health Vault MCP server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=YOUR_USER
Group=YOUR_GROUP
WorkingDirectory=/opt/health-vault
EnvironmentFile=/etc/health-vault.env
ExecStart=/opt/health-vault/.venv/bin/python -m health_mcp
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/health-vault/data
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now health-vault.service
systemctl is-active health-vault
curl -sS http://127.0.0.1:8002/healthz
4. Reverse proxy (TLS)
Terminate HTTPS on the proxy and reverse-proxy to 127.0.0.1:8002 (or the app host). Preserve Host and X-Forwarded-Proto.
Caddy example:
health.example.com {
reverse_proxy 127.0.0.1:8002
}
nginx sketch:
server {
listen 443 ssl;
server_name health.example.com;
# ssl_certificate ...;
location / {
proxy_pass http://127.0.0.1:8002;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Point DNS A/AAAA for health.example.com at your server (or your edge).
5. Data + Grok
cd /opt/health-vault
# if OAUTH_LOGIN_PASSWORD was not used:
HEALTH_ROOT=/opt/health-vault .venv/bin/health oauth set-password
HEALTH_ROOT=/opt/health-vault .venv/bin/health login
HEALTH_ROOT=/opt/health-vault .venv/bin/health sync --days 7
Optional cron (every 30 minutes):
*/30 * * * * cd /opt/health-vault && HEALTH_ROOT=/opt/health-vault .venv/bin/health sync --days 3 >>/var/log/health-sync.log 2>&1
Connect Grok Custom Connector to:
https://health.example.com/mcp
Smoke checks (C)
curl -sS -o /dev/null -w '%{http_code}\n' https://health.example.com/ # 200
curl -sS -o /dev/null -w '%{http_code}\n' https://health.example.com/healthz # 200
curl -sS https://health.example.com/.well-known/oauth-authorization-server | head
curl -sS -o /dev/null -w '%{http_code}\n' -X POST https://health.example.com/mcp \
-H 'Content-Type: application/json' -d '{}' # 401
Auth model
| Method | Use case |
|---|---|
Bearer API key (health key create) |
Grok CLI / local agents |
| OAuth 2.1 + PKCE + owner password | grok.com Custom Connector |
| Xiaomi QR token | Ingest only — never sent to Grok |
- API keys are stored as SHA-256 hashes; plaintext shown once.
- OAuth access/refresh tokens hashed in SQLite under
data/. - Unauthenticated
/mcp→ 401.
MCP tools
All tools return sync_health, attention_required, and a wellness disclaimer.
| Tool | Purpose |
|---|---|
health_status |
Last sync, coverage, token presence |
health_get_daily |
Daily rows (steps, sleep, HR, SpO2, stress, …), max 90 days |
health_context |
Compact brief for one question (question_hint) |
health_trends |
Mean / min / max / half-window delta for one metric |
health_anomalies |
Points outside mean ± 2σ |
If attention_required is true (stale/broken sync), Grok should warn first and suggest health login / health sync.
Env thresholds: HEALTH_STALE_AFTER_HOURS (default 36), HEALTH_BROKEN_AFTER_HOURS (default 72).
CLI reference
health login # Xiaomi QR → data/token.json
health sync [--days N] [--uid …] [--base-url …]
health status
health show [--days N]
health key create|list|revoke
health oauth set-password|status
health serve [--host] [--port] [--public-url] [--tunnel]
health tool <name> [--days N] [--hint …] [--metric …]
Production process entry: python -m health_mcp (reads env above).
Layout
src/health_core/ SQLite schema, queries, sync_health
src/health_ingest/ Xiaomi cloud → daily_metrics
src/health_auth/ API keys, owner password, OAuth AS
src/health_mcp/ MCP server, tools, tunnel helper
src/health_cli/ `health` CLI
vendor/mi-fitness-python/ vendored SDK (GPL-3.0)
deploy/ example systemd unit (template only)
docs/GROK_CONNECT.md Grok connection details
data/ runtime (gitignored)
Troubleshooting
| Symptom | What to try |
|---|---|
| Login QR invisible | Open data/qr_login.png; use Xiaomi Account app to scan |
Token not found / TokenExpiredError |
health login again |
| Sync OK but empty metrics | Sync the band in Mi Fitness on the phone; wait; re-run health sync |
not support HlthRelativesCareClient |
Expected on some accounts; own user_id path still works |
| Wrong region / empty cloud | Try --base-url / HEALTH_API_BASE if your account is not on default RU IDC |
| MCP always 401 with key | Use full Bearer hlt_… header; create a new key if revoked |
| grok.com cannot connect | Must be public HTTPS + path /mcp; OAuth password set; tunnel alive |
| Tunnel 530 / hostname fails | Restart tunnel; two-terminal setup; confirm cloudflared has network |
| Stale data in Grok answers | Check health status; run sync on a schedule |
attention_required in every tool |
Fix ingest; do not treat metrics as current until sync_health.status is ok |
Security notes
- Never commit
data/,.env, tokens, or passwords (see.gitignore). - Prefer long random
OAUTH_LOGIN_PASSWORD; rotate viahealth oauth set-password. - Expose only HTTPS to the internet; keep the app port private when possible.
- Single-owner design: one vault password / API keys for that host.
Acknowledgements
This project stands on reverse-engineering and open-source work that made Xiaomi Fitness data reachable outside the official app.
- alexgetmancom/miband-bot — personal self-hosted Telegram bot for Xiaomi Fitness / Mi Band (auth, cloud sync, SQLite, parsers). The approach and practical stack around Band 10 / Mi Fitness cloud were a major starting point for this vault. Thank you to the author for publishing the research and code. See also the write-up Reverse Engineering the Xiaomi Smart Band 10 (Habr).
- MistEO/MiSDK (
mi-fitness/mi_fitness) — Python SDK for Xiaomi Health / “relatives” APIs (QR login, RC4 transport, typed clients). Vendored undervendor/mi-fitness-python/(GPL-3.0).
Neither project is affiliated with this repo; any bugs here are ours.
License
This project is free software under the GNU General Public License v3.0 or later (GPL-3.0-or-later).
You may run, study, share, and modify it. If you distribute modified versions (or a product that combines this code with the vendored SDK), you must keep them under GPL-compatible terms and preserve copyright/license notices. See LICENSE for the full text.
- Includes
vendor/mi-fitness-python(MistEO/MiSDK), also GPL-3.0 — attribution required. - Unofficial Xiaomi Health protocol — may break without notice when Xiaomi changes apps or servers.
Optional reading
- docs/GROK_CONNECT.md — Grok auth modes and endpoints
- PLAN.md — architecture and design notes
- NOTES_EXPLORE.md — which metrics the API returned in testing
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。