Instalar local-fitness: Guía Completa y Verificada
Entrenador personal de running auto-alojado vía MCP — Instalación, configuración y operación segura
local-fitness es un entrenador personal de running auto-alojado que transforma datos Garmin Connect en informes diarios estructurados, evaluaciones estelares de sesiones y planes adaptativos mediante el Protocolo de Contexto del Modelo (MCP). Arquitectura agent-first: el servidor actúa como capa de transporte determinista; toda la síntesis conversacional ocurre en el cliente agente.
Fecha de verificación: 2026-10-05 | Licencia: MIT | Repositorio: https://github.com/natejswenson/local-fitness
Prerrequisitos y Requisitos del Sistema
Requisitos de Software
- Python 3.12+ — Instalar desde https://docs.python.org/3/
- uv (gestor de paquetes) — `curl -LsSf https://astral.sh/uv/install.sh | sh`
- Cuenta Garmin Connect con historial disponible
- Acceso a Claude Code o MCP compatible para trabajos programados
Plataformas Soportadas
| Plataforma | Método | Notas |
|---|---|---|
| macOS | Nativo + launchd | Plataforma primaria (Keychain, scheduler) |
| Linux | Nativo o Docker | Requiere configuración manual de credenciales |
| Windows | Docker recomendado | No soportado nativamente actualmente |
Dependencias del Sistema (Docker)
Si usas Docker, el Dockerfile instala automáticamente: curl, ca-certificates, nodejs, pango, fonts-dejavu para WeasyPrint.
Instalación Local (macOS/Linux)
Paso 1: Clonar Repositorio
git clone https://github.com/natejswenson/local-fitness.git
cd local-fitness
Paso 2: Instalar Dependencias
uv sync
Esto instala todas las dependencias de pyproject.toml incluyendo garminconnect, weasyprint y herramientas MCP.
Paso 3: Configuración Inicial (Opcional)
cp .env.example .env
Los valores por defecto funcionan para la mayoría de casos. Solo edita si necesitas personalizar:
LOCAL_FITNESS_API_TOKEN— Token de autenticación del servidor (ver sección Seguridad)LOCAL_FITNESS_DISPLAY_UNITS— Millas/min por defecto para runners anglosajones
Paso 4: Credenciales Garmin y DB Inicial
uv run fitness setup
Esto crea la base de datos SQLite local (data/fitness.db) y configura credenciales.
Paso 5: Pull Datos en Vivo
uv run fitness pull
Recupera datos desde Garmin Connect. Si la máquina estuvo apagada días, se recuperan automáticamente.
Paso 6: Backfill Histórico (Opcional)
uv run fitness backfill ~/Downloads/garmin-export.zip
Carga única desde export ZIP oficial de Garmin para razonar sobre toda la historia. Descarga desde Garmin Connect → Export → ZIP.
Paso 7: Recomputar Baselines + Training Load
uv run fitness recompute-baselines
Reconstruye líneas base de 60 días para resting HR, sleep, Body Battery, stress y calcula CTL/ATL/TSB.
Paso 8: Generar Primer Briefing
uv run fitness brief
Genera briefing diario estructurado → briefings/YYYY-MM-DD.json. También genera PDF con gráficos HR/pace y splits por milla.
Paso 9: (macOS) Instalar Trabajos Programados
./ops/install-launchd.sh
Configura launchd para ejecutar fitness brief diariamente. Credenciales en Keychain.
Paso 10: (Opcional) MCP sobre HTTP
uv run fitness serve # http://127.0.0.1:8765/mcp/
Exponer herramientas MCP sobre HTTP para integración con agentes remotos.
Configuración de Credenciales y Variables
Credenciales Garmin Connect
En macOS, las credenciales se guardan en .garminconnect/ dentro del Keychain. En Linux, usar variables de entorno o archivo .env:
export GARMIN_CLIENT_ID="tu-client-id"
export GARMIN_CLIENT_SECRET="tu-client-secret"
export GARMIN_REFRESH_TOKEN="tu-refresh-token"
Variables de Entorno Recomendadas
# En .env o exportar en shell
LOCAL_FITNESS_API_TOKEN="token-generado"
LOCAL_FITNESS_DISPLAY_UNITS="miles" # miles/min por defecto
GARMIN_CLIENT_ID="..."
GARMIN_CLIENT_SECRET="..."
GARMIN_REFRESH_TOKEN="..."
Generar Token de Seguridad
Para hosts no loopback, el token es obligatorio:
python -c "import secrets; print(secrets.token_urlsafe(32))"
Primeros Pasos: Pull, Baselines y Briefing
Comandos Principales de Uso Diario
# Pull desde última ejecución exitosa
fitness pull
# Backfill histórico (una vez)
fitness backfill <zip>
# Recomputar baselines + training load
fitness recompute-baselines
# Recomputar Body Battery específicamente
fitness recompute-body-battery
# Generar briefing diario
fitness brief
# Usar modelo Opus para una ejecución
fitness brief --opus
# Regenerar y enviar briefing por email
fitness brief-email
# Reconciliar Google Calendar con plan activo
fitness plan-calendar
# Flujo OAuth único de Google
fitness calendar-auth
# Leer/escribir settings-table key (ej. user_name)
fitness config set|get <key>
# Servir MCP sobre HTTP
fitness serve # http://127.0.0.1:8765/mcp/
# Servir herramientas a MCP client vía stdio
fitness mcp-stdio
# Estado de la DB y última ingestión
fitness status
Conexión MCP (Estándar)
El MCP es la única forma de interactuar con el coach; no hay UI web. Se exponen 48+ herramientas (47 sobre HTTP), 2 prompts y 2 recursos.
Conexión Local sin Token
claude mcp add --transport stdio fitness -- uv run fitness mcp-stdio
Conexión HTTP con Token
claude mcp add --transport http fitness https://<host>/mcp/ --header "Authorization: Bearer $TOKEN"
Prompts Principales
coach: ensambla snapshot diario completo (métricas vs baseline, training load, workouts recientes) + persona coach + preferencias guardadas.brief: compone briefing estructurado diario y lo persiste víasave_brief.
Despliegue con Docker
Requisitos del Sistema
- Docker Engine instalado y funcionando
- Volumes de datos montados correctamente
Configuración de Volúmenes
docker run -d \
--name local-fitness \
-v /ruta/local/data:/data \
-v /ruta/local/briefings:/briefings \
-v /ruta/local/.garminconnect:/home/app/.garminconnect \
-v /ruta/local/.claude:/home/app/.claude \
-v /ruta/local/.codex:/home/app/.codex \
-e LOCAL_FITNESS_API_TOKEN="tu-token" \
ghcr.io/natejswenson/local-fitness:latest
Dockerfile Oficial
El Dockerfile usa python:3.12-slim, instala curl, ca-certificates, nodejs, pango, fonts-dejavu para WeasyPrint. Clona @anthropic-ai/claude-code@2.1.119 y @openai/codex@0.157.1.
Requisito de Token en Docker
Requiere LOCAL_FITNESS_API_TOKEN cuando no está en loopback. Generar token con comando Python anterior.
Seguridad y Gestión de Tokens
Autenticación del Servidor
El servidor autentica cada ruta no pública (/health excluido) con token bearer LOCAL_FITNESS_API_TOKEN. En hosts no loopback, el token es obligatorio; el servidor se refusa a iniciar sin él.
Generar Token Seguro
python -c "import secrets; print(secrets.token_urlsafe(32))"
Rotación de Token
Rotación de token actualiza config del cliente; no hay sesión de navegador que re-promptear. Guarda el nuevo token y actualiza:
- Variables de entorno en
.env - Configuración del MCP client
- Docker environment variables
Privacidad por Diseño
La DB, briefings y logs permanecen locales; gitignored. El servidor no ejecuta inferencias de IA; toda la síntesis ocurre en el cliente MCP.
Actualización y Backup
Actualizar local-fitness
git pull
cd ..
uv sync
cd local-fitness
fitness recompute-baselines # Recomputar tras cambios de datos
Backup Completo
Copiar estos directorios antes de actualizar:
data/fitness.db— Base de datos SQLite con todos los datos Garminbriefings/— Briefings diarios generados (JSON + PDF).garminconnect/— Credenciales Garmin OAuth.claude/— Configuración del MCP client Claude.codex/— Configuración del MCP client Codex (si aplica)
Comando de Backup Rápido
tar -czvf local-fitness-backup-$(date +%Y%m%d).tar.gz \
data/fitness.db \
briefings/ \
.garminconnect/ \
.claude/ \
.codex/
Restaurar Backup
tar -xzf local-fitness-backup-YYYYMMDD.tar.gz
cd local-fitness
fitness recompute-baselines # Reconstruir baselines tras restaurar DB
Errores Habituales y Soluciones
| Error | Causa | Solución |
|---|---|---|
uv run fitness pull: Connection refused |
Garmin Connect API no accesible o token expirado | Re-autenticar en Garmin Connect → obtener nuevo refresh token → actualizar credenciales |
fitness brief: No data in database |
No se ha ejecutado fitness pull o backfill |
Ejecutar uv run fitness pull primero, luego fitness recompute-baselines |
fitness serve: Missing LOCAL_FITNESS_API_TOKEN |
Servidor no en localhost sin token | Generar token con Python y configurar LOCAL_FITNESS_API_TOKEN en entorno |
PDF generation failed: WeasyPrint error |
Fuentes o dependencias de renderizado faltantes | En Docker: verificar que fonts-dejavu y pango estén instalados. En nativo: instalar dependencias del sistema |
fitness recompute-baselines: Insufficient data |
Menos de 60 días de datos históricos | Ejecutar backfill con export ZIP completo de Garmin para obtener historial suficiente |
claude mcp add: Transport stdio failed |
Proceso fitness no está corriendo o token inválido | Ejecutar uv run fitness mcp-stdio primero, verificar que el proceso esté activo |
Verificación de Instalación Exitosa
# Verificar instalación
cd local-fitness
uv run fitness status
# Debería mostrar:
# - DB row counts
# - Last ingest run info
# - Baselines computed: True/False
# - Training load model: Banister CTL/ATL/TSB
FAQ Basada en Evidencia
- ¿Funciona sin suscripción a Claude?
- Sí, el servidor no ejecuta inferencias. El agente interactivo requiere MCP client con suscripción (Claude Code o compatible). La arquitectura agent-first mantiene la inferencia en el cliente.
- ¿Puedo usarlo en Linux?
- Sí, vía instalación nativa o Docker. macOS es plataforma primaria por conveniencia de launchd y Keychain, pero Linux funciona completamente con configuración manual de credenciales.
- ¿Cómo se calculan las estrellas de los workouts?
- Cada métrica (distance, pace, HR, continuity) reduce a una desviación relativa normalizada que pasa por una curva compartida en escala 1-5 con precisión fraccional. Implementado en Python (
agent/interpret.py). - ¿Los datos salen de mi máquina?
- No. La DB, briefings y logs permanecen locales; solo el agente lee métricas cuando lo solicita o escribe un briefing. Privacidad por diseño.
- ¿Qué es el modelo Banister CTL/ATL/TSB?
- Modelo determinista de carga de entrenamiento que calcula fitness (CTL), fatiga (ATL) y forma (TSB). Implementado en Python, no como black-box. Código verificable en
agent/interpret.py. - ¿Puedo personalizar los prompts del coach?
- Sí, la arquitectura agent-first expone 48+ herramientas MCP para prompts personalizados. Verifica documentación oficial de Anthropic para MCP.
- ¿Funciona con dispositivos Garmin que no reportan HRV Status (ej. Instinct Solar)?
- Funcionalidad limitada en estos dispositivos. El sistema requiere HRV Status para cálculos completos de recuperación.
- ¿Cómo genero PDFs de workouts?
- Ejecutar
fitness briefgenera automáticamente PDFs con gráficos HR/pace y splits por milla en formato PRESS-themed. Tambiénworkout_report_cardpara tarjetas estelares. - ¿Puedo automatizar el briefing diario?
- Sí, con
./ops/install-launchd.shen macOS o cron en Linux. El compositor es read-only para automatización segura. - ¿Qué alternativas existen?
- Soluciones SaaS como TrainingPeaks, Strava Segments Pro o Garmin Connect IQ ofrecen dashboards web y apps móviles nativas. local-fitness se diferencia por: análisis determinista verificable, privacidad total (todo local), arquitectura agent-first que integra con tu stack MCP existente, y capacidad de personalización mediante prompts.