{
  "markdown": "# coach-mcp\n\n<!-- mcp-name: io.github.snoozelieb/coach-mcp -->\n\n[![CI](https://github.com/snoozelieb/coach-mcp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/snoozelieb/coach-mcp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nAn opinionated AI training coach as an MCP server. It pulls your real data from\nGarmin Connect and **prescribes with authority** — science-based load management\n(ACWR), **code-enforced injury gates** (the server rejects plans that violate an\nactive injury restriction, no matter what the LLM says), and **persistent\ncoaching memory** so decisions, rationale, and your adaptation patterns survive\nbetween conversations. It will tell you \"no\" when your enthusiasm exceeds your\ncapacity.\n\nAll health data and credentials stay on your machine — see\n[Security & Privacy](#security--privacy).\n\n## Quickstart\n\nYou need Python 3.12+, a free Garmin Connect account, and an MCP client\n(Claude Code, Claude Desktop, or Cursor).\n\n### Option A: uvx (recommended)\n\nNo install step — your MCP client runs the server on demand:\n\n```bash\nuvx garmin-coach-mcp\n```\n\nJump to [Connect your MCP client](#connect-your-mcp-client) and use `uvx` as\nthe command.\n\n### Option B: from source\n\n```bash\ngit clone https://github.com/snoozelieb/coach-mcp.git\ncd coach-mcp\n\npython -m venv .venv\n# Linux/macOS:\nsource .venv/bin/activate\n# Windows:\n.venv\\Scripts\\activate\n\npip install -r requirements.txt\ncp .env.example .env   # then edit: GARMIN_EMAIL, GARMIN_PASSWORD\npython server.py\n```\n\n## Connect your MCP client\n\nThe server needs two environment variables: `GARMIN_EMAIL` and\n`GARMIN_PASSWORD`. Optional: `COACH_DATA_DIR` (where your coaching data lives)\nand `ANTHROPIC_API_KEY` (only for the standalone `daily_loop.py --llm` script).\nFrom a source checkout, a `.env` file works too.\n\n### Claude Code\n\n```bash\nclaude mcp add coach-mcp \\\n  --env GARMIN_EMAIL=you@example.com \\\n  --env GARMIN_PASSWORD=your_garmin_password \\\n  -- uvx garmin-coach-mcp\n```\n\nOr in `.mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"coach-mcp\": {\n      \"command\": \"uvx\",\n      \"args\": [\"garmin-coach-mcp\"],\n      \"env\": {\n        \"GARMIN_EMAIL\": \"you@example.com\",\n        \"GARMIN_PASSWORD\": \"your_garmin_password\",\n        \"COACH_DATA_DIR\": \"/path/to/your/coach-data\"\n      }\n    }\n  }\n}\n```\n\nRunning from source instead: `claude mcp add coach-mcp -- python /full/path/to/coach-mcp/server.py`\n\n### Claude Desktop\n\nIn `claude_desktop_config.json` (Settings → Developer → Edit Config):\n\n```json\n{\n  \"mcpServers\": {\n    \"coach-mcp\": {\n      \"command\": \"uvx\",\n      \"args\": [\"garmin-coach-mcp\"],\n      \"env\": {\n        \"GARMIN_EMAIL\": \"you@example.com\",\n        \"GARMIN_PASSWORD\": \"your_garmin_password\",\n        \"COACH_DATA_DIR\": \"/path/to/your/coach-data\"\n      }\n    }\n  }\n}\n```\n\n### Cursor\n\nIn `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):\n\n```json\n{\n  \"mcpServers\": {\n    \"coach-mcp\": {\n      \"command\": \"uvx\",\n      \"args\": [\"garmin-coach-mcp\"],\n      \"env\": {\n        \"GARMIN_EMAIL\": \"you@example.com\",\n        \"GARMIN_PASSWORD\": \"your_garmin_password\",\n        \"COACH_DATA_DIR\": \"/path/to/your/coach-data\"\n      }\n    }\n  }\n}\n```\n\nIf you installed with `pip install garmin-coach-mcp` instead of uvx, use\n`\"command\": \"garmin-coach-mcp\"` with no args in any of the blocks above.\n\n## First run\n\n1. **Create your profile.** From a source checkout, run the interactive wizard:\n\n   ```bash\n   python scripts/setup_wizard.py\n   ```\n\n   It creates your athlete profile, training config, and empty plan/memory\n   files in the data directory. Alternatively, create the two required files\n   by hand and let the coach fill in the rest via conversation:\n\n   ```bash\n   echo '{\"personal\":{\"name\":null},\"injury_history\":[],\"life_constraints\":{}}' > data/athlete.json\n   echo '{\"events\":[],\"current_block\":{\"phase\":\"base\"}}' > data/training_config.json\n   ```\n\n2. **Pull your Garmin baseline.** In your MCP client, say:\n\n   > \"Run refresh_athlete_baseline and set up my training.\"\n\n   The coach pulls your name, weight, age, HR data, and training capacity from\n   Garmin, then starts the onboarding conversation — goals, constraints,\n   injury history, race calendar.\n\n3. **Garmin MFA / expired session.** Garmin logins are token-cached. If tools\n   start returning `AUTH_REQUIRED`, recover with:\n\n   ```bash\n   python scripts/garmin_login.py\n   ```\n\n   It does a fresh credential login, prompts for the MFA code if Garmin asks,\n   and saves new tokens. Restart the MCP server afterwards.\n\n## How it works\n\n1. **Snapshot first** — every coaching conversation starts from\n   `get_coaching_snapshot()`: current time context, 7-day week grid (rest days\n   explicit), fitness metrics, plan adherence, open anomalies, injuries, sleep\n   gate.\n2. **Load hierarchy before prescribing** — overall ACWR (injury gate, 0.8–1.3\n   sweet spot), then sport-specific ACWR (spike detection), then\n   sport-specific CTL (race readiness).\n3. **Hard gates are code, not vibes** — `update_weekly_plan` and\n   `push_plan_to_garmin` reject sessions that violate an active injury's\n   restricted activities, and every non-rest session must carry a `purpose` or\n   the save is refused.\n4. **Curiosity with memory** — planned-vs-actual anomalies (missed session,\n   type mismatch, activity on a rest day) register once with a lifecycle\n   (open → asked → resolved); the coach asks you what happened instead of\n   silently assuming.\n5. **Everything persists** — decisions, approvals, adaptation patterns, and\n   season lifecycle (race debriefs, phase transitions) live in local JSON and\n   carry across sessions.\n\n## MCP surface\n\n49 tools — you don't call them directly; the coach uses them during\nconversation:\n\n| Category | Tools |\n|----------|-------|\n| **Coaching core** | `get_coaching_snapshot` (canonical, sectioned), `get_compliance_report`, `get_coaching_score` |\n| **Planning** | `get_weekly_plan`, `update_weekly_plan`, `push_plan_to_garmin`, `get_week_constraints`, `get_weekly_prescription`, `get_periodization_status`, `update_phase` |\n| **Garmin data** | `query_metrics` (kind=fitness/intensity/daily/readiness/personal_records), `get_activities_range` |\n| **Athlete** | `get_athlete`, `update_athlete`, `set_ftp`, `set_threshold_pace`, `analyze_ftp_test`, `refresh_athlete_baseline`, `refresh_fitness_history`, `get_onboarding_guide` |\n| **Methodology** | `get_methodology`, `update_methodology` |\n| **Races** | `races` (action=list/add/update/research), `remove_race` |\n| **Strength** | `sync_strength_session`, `get_strength_baseline`, `approve_progression`, `set_exercise_preference`, `generate_strength_workout`, `add_exercise` |\n| **Injuries** | `diagnose_injury`, `research_injury`, `update_injury_status` |\n| **Research** | `research_exercise`, `list_exercises`, `research_sport` |\n| **Memory** | `log_coaching_decision`, `get_active_decisions`, `update_decision_status`, `record_athlete_response`, `get_response_patterns`, `resolve_anomaly` |\n| **Approvals** | `propose_coaching_action`, `list_pending_approvals`, `approve_proposal`, `reject_proposal` |\n| **Interactive** | `generate_smart_brief`, `interactive_check_in` |\n\nEvery tool carries MCP annotations (read-only / destructive / idempotent /\nopen-world), enforced by tests.\n\n**5 prompts**: `weekly_planning`, `morning_brief`, `injury_assessment`,\n`week_review`, `onboarding`.\n\n**6 resources**: `coach://athlete/profile`, `coach://plan/current`,\n`coach://config/training`, `coach://coaching/decisions`, `coach://context/now`,\n`coach://coaching/doctrine` (the long-form coaching doctrine).\n\n## Security & Privacy\n\nEverything stays on your machine:\n\n- **Credentials**: `GARMIN_EMAIL`/`GARMIN_PASSWORD` live in your MCP client\n  config or a local `.env`. Garmin OAuth tokens are cached in a local token\n  store (`.garth/` in a source checkout, a per-user `garmin-tokens` directory\n  for installed copies; override with `COACH_TOKEN_DIR`).\n- **Health data**: all coaching data (profile, plans, fitness history, sleep,\n  coaching memory) is local JSON in your data directory. There is no backend,\n  no telemetry, no analytics.\n- **What leaves your machine**: requests to Garmin's own API (your\n  credentials/tokens, sent only to Garmin); whatever your MCP client sends to\n  its LLM as part of the conversation; optional public web-page fetches when\n  the coach researches a race, injury, or exercise; and, only if you run\n  `daily_loop.py --llm`, one request to the Anthropic API.\n- **Single athlete per data directory** by design. For multiple athletes, run\n  separate server instances with separate `COACH_DATA_DIR`s.\n\nSee [SECURITY.md](SECURITY.md) for details and how to report issues.\n\n### Data directory\n\nResolution order: `COACH_DATA_DIR` env var → `data/` in a source checkout → a\nper-user data directory (created on first run for installed packages). The\nonly file shipped with the package is `methodology.json` (safety rules, race\ntemplates, personas); everything personal is created locally and never\ncommitted.\n\n## Advanced\n\n```bash\n# HTTP transport (streamable-http) instead of stdio\nCOACH_TRANSPORT=streamable-http FASTMCP_PORT=8000 garmin-coach-mcp\n\n# Code Mode (search/execute meta-tools instead of 49 individual tools)\npip install fastmcp[code-mode]\nCOACH_CODE_MODE=1 garmin-coach-mcp\n\n# Standalone morning audit\npython scripts/daily_loop.py          # template-based brief\npython scripts/daily_loop.py --llm    # LLM brief (needs ANTHROPIC_API_KEY)\n\n# Tests (1,333 tests; clean checkouts use committed sanitized fixtures)\npip install -r requirements-dev.txt\npython -m pytest -q\n```\n\n## Architecture\n\n`server.py` registers tools from the `coach/` package (11 tool modules, pure\nparsers, a typed pydantic storage layer, CTL/ATL/ACWR fitness math, a Garmin\nclient with token-first auth, and a workout builder that pushes structured\nworkouts to your watch). The project went through a five-phase modernization —\nauth rebuild, schema layer, hard gates, sectioned snapshot, packaging — whose\nfull history and rationale live in\n[docs/UPGRADE_ROADMAP.md](docs/UPGRADE_ROADMAP.md). Development conventions\nare in [CLAUDE.md](CLAUDE.md).\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 10184,
  "sha": "6a6ead0af792ad108087f029c3c6cfa2647df014af38f8e8e6d6624c2f706e16",
  "repo_slug": "snoozelieb/coach-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_snoozelieb_coach_mcp_1ed94ac8/readme"
}