Vaultbeat — Apple Health MCP
Datos de Apple Health cifrados de extremo a extremo para tu propio agente de IA: sueño, HRV, frecuencia cardíaca, entrenamientos, ciclo. Sincronización en segundo plano desde el iPhone, descifrados localmente.
Documentación
Servidor MCP de Vaultbeat Apple Health
Tus datos de Apple Health — etapas del sueño, ciclo, HRV, frecuencia cardíaca en reposo, entrenamientos, peso, VO₂ máx, comidas, levantamientos, notas, síntomas — legibles y escribibles por tu propio agente de IA (Claude Code, Claude Desktop, Codex, Hermes, OpenClaw, cualquier cosa que hable MCP), cifrados de extremo a extremo para que solo tu máquina vea el texto plano.
La aplicación para iPhone Vaultbeat lee HealthKit y cifra cada registro en el teléfono. Este paquete es el servidor local con el que habla tu agente: descarga el texto cifrado, lo descifra en tu máquina y lo sirve a través de MCP.
iPhone (HealthKit → Vaultbeat app, encrypts) ──► cloud (ciphertext only) ──► this server (decrypts locally) ──MCP──► your agent
La nube almacena texto cifrado que no puede leer. La clave que lo abre se genera en tu máquina cuando emparejas y nunca la abandona.
Requisitos
- La aplicación iOS de Vaultbeat, versión 1.2.3 o posterior, con sesión iniciada — App Store
- uv (para
uvx). El servidor necesita Python 3.11+ yuvxdescarga un intérprete adecuado por sí mismo si tu Python del sistema es más antiguo. - Vaultbeat Pro para acceso de agente. Emparejar una máquina inicia una prueba de 3 días de la interfaz de IA; después, las lecturas y escrituras necesitan Pro, comprado en la aplicación iOS (Configuración → Membresía). Cuando el acceso caduca no se borra nada, y comprar Pro reanuda esta máquina sin volver a emparejar.
Cuánto historial puede leer este servidor lo decide lo que la aplicación sube. Con Pro, es todo tu historial. Una prueba sube todo tu historial en la aplicación 1.2.9 y posteriores, y tus últimos 7 días en 1.2.8 y anteriores. Sin ninguno de los dos, la aplicación 1.2.8 y anteriores sube tus últimos 7 días y la aplicación 1.2.9 y posteriores no sube nada. Así que un historial corto suele ser un límite del plan, no un retraso de sincronización — vaultbeat_doctor distingue las causas.
Inicio rápido
1. Empareja esta máquina con tu iPhone.
uvx vaultbeat-apple-health@latest bind
Imprime un código QR y espera. En la aplicación Vaultbeat abre pestaña MCP → Conectar un servidor de IA (aplicación 1.2.8 y anteriores: Configuración → Datos e IA) y escanéalo. El comando espera 5 minutos (--timeout para cambiar); una vez escaneado hay 10 minutos para terminar.
2. Añade el servidor a tu cliente MCP.
Claude Code:
claude mcp add vaultbeat-health -- uvx vaultbeat-apple-health@latest serve --transport stdio
Claude Desktop — añade a claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json) y reinícialo:
{
"mcpServers": {
"vaultbeat-health": {
"command": "uvx",
"args": ["vaultbeat-apple-health@latest", "serve", "--transport", "stdio"]
}
}
}
Claude Desktop no hereda el PATH de tu shell. Si informa que no se puede encontrar uvx, pon la ruta absoluta de which uvx en "command".
Cualquier otro cliente MCP: ejecuta uvx vaultbeat-apple-health@latest serve --transport stdio como servidor stdio. El @latest importa — sin él, uvx sigue ejecutando la versión que haya cacheado primero.
3. Comprueba que funciona. Pide a tu agente que llame a vaultbeat_doctor, o ejecuta:
uvx vaultbeat-apple-health@latest doctor
Comprueba la configuración, la clave privada, la accesibilidad de la nube, el emparejamiento y un ciclo real de descarga y descifrado, y lista qué tipos de datos no tienen nada que leer todavía. Justo después de emparejar, el teléfono aún está sellando tu historial para la nueva máquina, así que algunos tipos pueden estar vacíos durante un tiempo; pestaña MCP → Re-sincronizar todos los datos de salud a IA en la aplicación (aplicación 1.2.8 y anteriores: Configuración → Datos e IA) lo acelera.
Pruébalo sin un iPhone
uvx vaultbeat-apple-health@latest --demo doctor
uvx vaultbeat-apple-health@latest --demo serve --transport stdio # wire this into a client
--demo es una bandera global — va antes del subcomando — y sirve un conjunto de datos sintéticos fijo: sin emparejamiento, sin nube, sin clave. Cada resultado dice demo_mode: true y lleva un banner de [SYNTHETIC DEMO DATA], y las herramientas de escritura log_* se niegan.
Herramientas MCP
26 herramientas. Las lecturas devuelven tus propios datos — la cuenta que emparejó esta máquina. Las herramientas marcadas como partner también aceptan partner=true para leer lo que tu pareja eligió compartir contigo en su aplicación — sueño, agua y peso, y ciclo, síntomas y notas solo si los activó. Las dos personas nunca se mezclan en un resultado. Cada herramienta de lectura acepta fresh=true para omitir la caché local.
Diagnóstico
| Herramienta | Qué hace |
|---|---|
vaultbeat_doctor | Diagnostica esta instalación de extremo a extremo, informa qué tipos de datos no tienen datos y las razones probables, y muestra el estado del emparejamiento. La herramienta a la que llamar antes de decirle a alguien que sus datos faltan. |
Registros de salud
| Herramienta | Qué devuelve |
|---|---|
get_sleep_nights partner | Cada noche como una fila compacta: hora de acostarse y despertarse, sueño, profundo / REM / núcleo / despierto, despertares, sueño ininterrumpido más largo, frecuencia cardíaca y respiratoria mientras duerme, siestas. Un año cabe en una llamada; since="YYYY-MM-DD" para una ventana de calendario. |
get_sleep_detail partner | Una o dos noches en profundidad: intervalos de etapas con frecuencia cardíaca y respiratoria por etapa. Las noches más recientes, o cualquier noche por fecha (since / until). |
get_menstrual_cycle partner | Muestras de ciclo y una predicción del próximo período. Sensible. |
get_symptoms partner | Síntomas de Apple Health, y junto a ellos los episodios que una persona informó (tipo, gravedad, inicio y fin, lugar, desencadenantes sospechosos). Sensible. |
get_notes partner | Notas de texto libre en días, con quién las escribió. Sensible. |
get_strength_log | Sesiones de fuerza: ejercicios, series × repeticiones, volumen por sesión. |
get_food_log | Comidas y elementos por día, con kcal / proteína / grasa / carbohidratos opcionales. Los 14 días más recientes que tienen un registro por defecto (no una quincena de calendario), o una ventana de since / until. |
get_workouts | Entrenamientos: tipo, duración, calorías, distancia. |
get_user_profile | Sexo, edad, fecha de nacimiento y altura. Necesita Vaultbeat para iOS 1.2.9 o posterior. |
Números diarios y análisis
| Herramienta | Qué devuelve |
|---|---|
get_metric partner | Valores por día para una serie, varias o todas: duración del sueño, horario y etapas, frecuencia cardíaca en reposo, HRV, temperatura de muñeca, VO₂ máx, peso y composición corporal, agua, pasos, energía activa / basal / total, minutos de ejercicio y de pie, distancia, atención plena. aggregation (avg / sum / min / max / latest) y granularity (day / week / month / weekday) se calculan en el servidor; since / until para una ventana de calendario. Hoy en una serie acumulativa se marca como parcial y se mantiene fuera de los agregados. |
get_intraday | Muestras dentro de un día, para HRV: una fila por hora que tenga muestras (granularity="hourly") o una por muestra ("raw"). Alrededor de 8–14 filas al día; limit cuenta filas. |
list_metric_series partner | Cada nombre de serie que aceptan las herramientas anteriores y posteriores, con su unidad y cuántos datos la respaldan. |
get_metric_trend partner | Pendiente de mínimos cuadrados, extremos, media / mediana / mín / máx. |
compare_metric_periods partner | Los N días más recientes contra los N anteriores, o dos ventanas nombradas. |
correlate_metric_series partner | r de Pearson entre dos series sobre los días que tienen ambas; lag_days empareja un día con uno posterior. |
Las herramientas de análisis devuelven solo números — sin puntuaciones, calificaciones o veredictos — y se niegan en lugar de ajustar una línea a dos puntos.
Escrituras — todas en la cuenta que emparejó esta máquina, y en ningún otro lugar.
| Herramienta | Qué hace |
|---|---|
log_food_append / log_strength_append / log_note_append | Añadir a un día. No puede borrar nada — el valor predeterminado seguro cuando un día ya puede tener entradas. |
log_food_entry / log_strength_entry / log_note | Reemplaza todo ese día con lo que se pasa, e informa exactamente qué se eliminó. |
log_weight_entry | Registra un pesaje, manteniendo la composición corporal de ese día. |
log_symptom | Registra un síntoma que la persona informa, como campos estructurados que una lectura posterior puede comparar con sueño, comida y frecuencia cardíaca. |
update_symptom / delete_symptom | Cambia un síntoma informado (generalmente su hora de finalización), o borra uno registrado por error. |
log_note y log_note_append aceptan partner=true para registrar una nota sobre tu pareja. Permanece en tu propia cuenta, nunca se envía a ellos y se mantiene aparte de tus propias notas.
Leyendo los resultados
- Cada lectura lleva un bloque
coverage.days_coveredes cuántos días distintos respalda la respuesta — cítalo junto a cualquier promedio o tendencia.more_available: truesignifica que existen registros más antiguos y tulimitse detuvo antes de ellos (oldest_availabledice hasta dónde llegan); nunca es una señal de datos faltantes. - Los resultados están limitados. Un resultado mayor de aproximadamente 60 KB no se envía; la herramienta devuelve
result_too_largecon una sugerencia para reducir la solicitud (unlimitmás pequeño, una fecha desince, una serie en lugar de todas).get_metricyget_intradaydevuelven sus filas como una tabla compacta (columns+rows). - Los argumentos desconocidos se rechazan. Un parámetro mal escrito o retirado es un error, nunca se ignora silenciosamente.
Prompts MCP
El servidor también sirve ocho prompts a través de prompts/list — daily_brief, sleep_review, energy_balance, training_block_review, cycle_aware_read, partner_check_in, log_from_conversation y why_is_this_empty. Cada uno nombra las herramientas a llamar y lleva las mismas dos reglas: di qué cubren los datos antes de concluir, y trata un resultado vacío como algo que explicar (tiene varias causas posibles), nunca como un cero. Cada argumento es opcional.
Privacidad y seguridad
- El descifrado ocurre solo en esta máquina. La nube contiene texto cifrado y claves envueltas por destinatario; la clave privada que las abre se genera aquí cuando emparejas.
- Los datos de salud salen de este paquete solo a través de MCP. Los subcomandos de línea de comandos emparejan una máquina, informan sobre ese emparejamiento e inician el servidor; ninguno imprime datos de salud.
- Categorías sensibles. Tu ciclo y los síntomas que Apple Health registra llegan a este servidor solo después de que los actives, por categoría, en la aplicación iOS — están desactivados por defecto. Tus notas y los síntomas que informas tú mismo (en la aplicación, o a través de
log_symptom) siempre son legibles por tu propio agente. Ninguno de estos llega al agente de tu pareja a menos que actives compartirlos con ellos. - Desempareja en cualquier momento desde la pestaña MCP de la aplicación (aplicación 1.2.8 y anteriores: Configuración → Datos e IA); la máquina ya no puede descifrar nada nuevo.
- Los resultados de las herramientas nunca contienen la clave privada ni el token del servidor.
Dónde vive la clave privada
No en config.json. Se busca en este orden:
- la variable de entorno
VAULTBEAT_PRIVATE_KEY, si está configurada (lectura, nunca escrita de vuelta) — para operadores que la inyectan desde un almacén de secretos; - el llavero del sistema — el caso normal en un escritorio;
~/.tether/mcp-local/identity.key, modo0600— el respaldo automático en una máquina sin llavero.
~/.tether/mcp-local/config.json (modo 0600) contiene el token del servidor emitido por la nube y tu clave pública. .tether es el nombre anterior de la aplicación y la ruta se mantiene a propósito: las instalaciones existentes encuentran su clave a través de ella.
Nunca borres
config.jsonpara "empezar limpio". La clave privada no está en él, así que borrarlo no limpia una clave mala — acuña una nueva identidad, y cada registro ya cifrado para la antigua se vuelve permanentemente ilegible. Para reparar un emparejamiento, ejecutabindde nuevo: re-empareja esta máquina en su lugar y conserva lo que ya está cifrado para ella.
Servidores sin cabeza. Si el llavero es inalcanzable, deja que el respaldo identity.key lo maneje — se activa por sí mismo. No configures PYTHON_KEYRING_BACKEND al backend nulo: acepta escrituras y no almacena nada, así que solo oculta un llavero al que podrías llegar. Si existe una sesión D-Bus pero este proceso no puede verla (común cuando el servidor lo inicia un marco de agente, systemd o cron), pasa DBUS_SESSION_BUS_ADDRESS explícitamente — resuélvelo primero (echo "unix:path=/run/user/$(id -u)/bus") y pon el resultado literal en el bloque env del cliente, ya que ese bloque es JSON, no un shell.
Telemetría
El servidor envía eventos de uso a PostHog (UE): qué herramienta fue llamada, si tuvo éxito,
un rango de duración, y las versiones del cliente de IA y del paquete — vinculados a la cuenta con la que esta máquina está
emparejada. Los argumentos de las herramientas, los resultados y los datos de salud nunca se envían. Establece VAULTBEAT_TELEMETRY=0
(o DO_NOT_TRACK=1) para desactivarlo. Detalles: vaultbeat.app/privacy.
Solución de problemas
Comienza con doctor. uvx vaultbeat-apple-health@latest doctor imprime una lista de [OK] / [FAIL] con una solución para lo primero que esté roto; doctor --json es el mismo informe para un agente. El código de salida 0 significa que está sano.
Una lectura devuelve resultados cortos o vacíos. Lee coverage.more_available primero: true significa que tu limit fue el límite — pide más. false con un historial corto es el límite del plan descrito en Requisitos, o un emparejamiento que aún se está completando. vaultbeat_doctor enumera los tipos sin datos y las posibles razones para cada uno.
El código QR se ve mal (mojibake o filas desiguales — generalmente Windows con una página de códigos no UTF-8). El emparejamiento aún está en espera, así que no vuelvas a ejecutar bind: eso reemplaza el código en pantalla. En su lugar, renderiza el payload JSON impreso justo encima del código como una imagen (qrencode -o pair.png '<payload>'), envíalo a tu teléfono y usa importar desde Fotos en el escáner de la aplicación; o ejecuta chcp 65001 en una terminal nueva; o usa bind --no-qr para el payload como texto.
Las lecturas fallan con un mensaje de acceso. La membresía de prueba o Pro ha caducado. Nada ha sido eliminado; comprar Pro en la aplicación de iOS (Configuración → Membresía) reanuda esta máquina sin volver a emparejar.
Transporte HTTP
Para clientes que se conectan a través de una red en lugar de lanzar un subproceso:
vaultbeat-apple-health serve --generate-token # mint and store a bearer token, print client config
vaultbeat-apple-health serve --transport http # 127.0.0.1:8000/mcp, bearer token required
El token se lee desde VAULTBEAT_MCP_HTTP_TOKEN (preferido, lo mantiene fuera del historial del shell) o de la configuración almacenada, y los clientes lo envían como Authorization: Bearer <token>. --show-token imprime el almacenado. --no-token sirve loopback sin autenticación.
Vincular más allá de loopback (--host 0.0.0.0 para una LAN o VPS) falla de forma segura: necesita tanto un token como --allow-remote. El token cruza el cable en texto claro, así que pon TLS delante (Caddy, nginx, Cloudflare). Otras banderas: --sse-response para respuestas estilo SSE, --stateful-http para clientes que necesitan sesiones.
Ejemplo mcp.json (estilo VS Code / Cursor):
{
"servers": {
"vaultbeat-health": {
"type": "http",
"url": "http://127.0.0.1:8000/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
Un cliente que solo habla stdio puede alcanzar el servidor HTTP a través de mcp-remote: npx -y mcp-remote http://127.0.0.1:8000/mcp --header "Authorization: Bearer <token>".
Reportar problemas
Este repositorio acepta problemas tanto para el servidor MCP como para la aplicación de iPhone Vaultbeat —
fallos de instalación, errores de herramientas, vinculación que nunca se completa, doctor reportando
algo incorrecto, y también problemas de la aplicación (UI, suscripciones, avisos de permisos de HealthKit,
sincronización que no aparece en el teléfono).
Abre un problema
y elige la plantilla que se ajuste. Preguntas e ideas pueden ir en
Discusiones.
Este es un repositorio público: nunca pegues datos de salud, códigos de emparejamiento o detalles de cuenta.
Desarrollo
La línea de comandos tiene seis subcomandos, y ninguno de ellos lee datos de salud:
vaultbeat-apple-health bind # pair this machine with the iOS app (QR)
vaultbeat-apple-health status # local pairing state
vaultbeat-apple-health doctor # self-diagnosis
vaultbeat-apple-health init # generate a keypair and config without pairing
vaultbeat-apple-health poll # poll once for a pending pairing
vaultbeat-apple-health serve # run the MCP server (stdio or http)
Los registros descifrados se almacenan en caché por tipo de datos bajo ~/.tether/mcp-local/cache/ (archivos 0600, directorio 0700) durante 600 segundos; VAULTBEAT_MCP_CACHE_TTL lo anula (0 lo desactiva). Después de la primera lectura, una actualización descarga solo los registros que cambiaron. Emparejar con una identidad de servidor diferente limpia la caché.
Ejecuta las comprobaciones desde un clon:
uv sync --frozen --all-extras
uv run --frozen pytest -q tests
uv run --frozen ruff check src tests
uv run --frozen mypy src
vaultbeat-mcp y vaultbeat-mcp-local permanecen como alias de script de consola para instalaciones de antes de que el paquete fuera renombrado vaultbeat-apple-health (0.6.2).