{
  "markdown": "# Garmin Workouts MCP\n\n`garmin-workouts-mcp` is a standalone MCP server for Garmin Connect workouts.\n\nIt is intended as a focused extension for workflows that need a bit more structure around Garmin workout payloads, especially strength training.\n\nThis project is packaged as a stdio MCP server and can be published as an OCI image for MCP registries and Glama deployment. It is not a standalone public HTTP MCP endpoint.\n\n<a href=\"https://glama.ai/mcp/servers/pranciskus/garmin-workouts-mcp\">\n  <img width=\"380\" height=\"200\" src=\"https://glama.ai/mcp/servers/pranciskus/garmin-workouts-mcp/badge\" alt=\"Garmin Workouts MCP server\" />\n</a>\n\n## Additions\n\n- Supports Garmin strength workout steps with `reps` end conditions.\n- Supports exercise metadata via explicit Garmin enums or friendly aliases.\n- Adds `preview_workout_payload` so payloads can be inspected before upload.\n- Adds `validate_workout` for early schema and mapping errors.\n- Adds `resolve_supported_strength_exercise` for quick mapping checks.\n- Adds `get_workout_input_schema` for machine-readable client integration.\n- Includes `walking` as a supported sport type, which is also reflected in the prompt/schema.\n- Keeps the familiar list/get/delete/schedule/calendar/activity tools.\n\n## Environment\n\nGarmin-backed tools authenticate lazily when they are called:\n\n- Authentication path: `GARMIN_EMAIL` and `GARMIN_PASSWORD`\n\nThe server can start without credentials. Tools that do not talk to Garmin, such as payload preview and schema inspection, still work without secrets.\n\n## Workout Input\n\nThe upload and preview tools accept a JSON object shaped like this:\n\n```json\n{\n  \"name\": \"Upper Day\",\n  \"type\": \"strength\",\n  \"steps\": [\n    {\n      \"stepType\": \"warmup\",\n      \"endConditionType\": \"lap.button\",\n      \"stepDescription\": \"General warm-up\"\n    },\n    {\n      \"stepType\": \"interval\",\n      \"exercise\": \"incline db press\",\n      \"endConditionType\": \"reps\",\n      \"stepReps\": 8,\n      \"stepDescription\": \"8-10 reps\"\n    },\n    {\n      \"stepType\": \"rest\",\n      \"endConditionType\": \"time\",\n      \"stepDuration\": 120\n    }\n  ]\n}\n```\n\nFor strength exercises, either pass a friendly alias:\n\n```json\n{ \"exercise\": \"t bar row\" }\n```\n\nor explicit Garmin enums:\n\n```json\n{\n  \"exercise\": {\n    \"category\": \"ROW\",\n    \"exerciseName\": \"T_BAR_ROW\"\n  }\n}\n```\n\nYou can also inspect the accepted structure programmatically through `get_workout_input_schema`, or resolve likely Garmin strength mappings with `resolve_supported_strength_exercise`.\n\n## Development\n\nRun tests in Docker Compose:\n\n```bash\ndocker compose run --rm tests\n```\n\nBuild the runtime image:\n\n```bash\ndocker build -t garmin-workouts-mcp:local .\n```\n\nSmoke test the stdio server startup without Garmin credentials:\n\n```bash\npython - <<'PY'\nimport subprocess\n\nproc = subprocess.Popen(\n    [\"bash\", \"-lc\", \"tail -f /dev/null | docker run --rm -i garmin-workouts-mcp:local\"]\n)\ntry:\n    proc.wait(timeout=5)\n    print(f\"container exited early with code {proc.returncode}\")\nfinally:\n    if proc.poll() is None:\n        proc.terminate()\n        proc.wait()\n        print(\"container stayed up for 5 seconds\")\nPY\n```\n\n## Publishing\n\nThe intended OCI image location is:\n\n```text\nghcr.io/pranciskus/garmin-workouts-mcp\n```\n\nRegistry metadata lives in [`server.json`](./server.json). The OCI image carries the required label:\n\n```text\nio.modelcontextprotocol.server.name=io.github.pranciskus/garmin-workouts-mcp\n```\n\nGlama ownership metadata lives in [`glama.json`](./glama.json). It declares the GitHub maintainer account that can claim and manage the Glama listing.\n\n",
  "bytes": 3583,
  "sha": "9f251e2c51727181d07d8b6266f5c9aa20d1b6d90a113e40b28e7c8eed4d7b44",
  "repo_slug": "pranciskus/garmin-workouts-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_pranciskus_garmin_workouts_mcp_bbcd1526/readme"
}