Health Vault
Provides tools to access and analyze health data from Xiaomi Mi Bands and Mi Fitness app, including daily steps, sleep, heart rate, SpO2, stress, and trends, via an unofficial Xiaomi Health API.
Click on "Install 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., "@Health VaultHow did I 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.
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 dataNot 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 ( |
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 |
Related MCP server: Garmin MCP
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 142. 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 87873. 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 7Scenario 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 page2. 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 --tunnelCopy 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.comFree 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 tokenScenario 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.targetsudo systemctl daemon-reload
sudo systemctl enable --now health-vault.service
systemctl is-active health-vault
curl -sS http://127.0.0.1:8002/healthz4. 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 7Optional 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>&1Connect Grok Custom Connector to:
https://health.example.com/mcpSmoke 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 '{}' # 401Auth model
Method | Use case |
Bearer API key ( | 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 |
| Last sync, coverage, token presence |
| Daily rows (steps, sleep, HR, SpO2, stress, …), max 90 days |
| Compact brief for one question ( |
| Mean / min / max / half-window delta for one metric |
| 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 |
|
|
Sync OK but empty metrics | Sync the band in Mi Fitness on the phone; wait; re-run |
| Expected on some accounts; own |
Wrong region / empty cloud | Try |
MCP always 401 with key | Use full |
grok.com cannot connect | Must be public HTTPS + path |
Tunnel 530 / hostname fails | Restart tunnel; two-terminal setup; confirm |
Stale data in Grok answers | Check |
| Fix ingest; do not treat metrics as current until |
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
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseBqualityCmaintenanceMCP server that provides local caching, sync, and tools for Zepp Life health data including steps, sleep, heart rate, workouts, and body measurements, supporting both file exports and cloud session access.Last updated105MIT
- AlicenseBqualityAmaintenanceLocal-first MCP server that connects AI agents to your Garmin sleep, HRV, Body Battery, stress, training readiness and activities, keeping tokens on your machine.Last updated412397MIT
- AlicenseAqualityBmaintenanceMCP server to read daily activity, sleep, heart rate, and body metrics from Google Health API, allowing AI assistants like Claude to access your health data. Optionally syncs health metrics to an Obsidian vault.Last updated5MIT
- Alicense-qualityDmaintenanceMCP server for Mi Fitness cloud data. Provides a local SQLite-backed server to sync and query daily activity, heart rate, and body measurements.Last updated3MIT
Related MCP Connectors
MCP server for Withings health data — sleep, activity, heart, and body metrics.
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ilyanehay/mi-health-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server