
A Python CLI tool that schedules marathon training plans from markdown files to Garmin Connect using the Garmin Workouts MCP server.- Parses structured markdown tables containing training plan data
- Converts natural language workout descriptions to Garmin-compatible format
- Schedules workouts automatically to Garmin Connect
- Idempotent scheduling: Skips workouts that already exist and match
- Post-scheduling validation: Verifies all workouts are correctly scheduled
- Provides dry-run mode for testing
- Beautiful terminal output with progress tracking
- Retry logic for failed operations
- Install the package with its dependencies:
- Set up Garmin Connect credentials (same as for the MCP server):
Schedule a training plan from a markdown file: python -m garmin_workouts_mcp.schedule_training_plan training_plan.md
Preview what will be scheduled without making changes: python -m garmin_workouts_mcp.schedule_training_plan training_plan.md --dry-run
Get detailed logging information: python -m garmin_workouts_mcp.schedule_training_plan training_plan.md --verbose
The training plan should be formatted as markdown tables with the following columns: Date | Day | Session | Distance | Heart Rate Target | Garmin MCP Description | Time |
---|
Jul 21 | Mon | Recovery Run | 8km | Zone 1: <125 bpm | "8km recovery run at zone 1 heart rate under 125bpm" | 7:00 AM |
- Date: Format as "Mon DD" (e.g., "Jul 21")
- Day: Day of week
- Session: Name of the workout
- Distance: Distance with unit (e.g., "8km", "21km")
- Heart Rate Target: Target heart rate or zone
- Garmin MCP Description: Natural language description in quotes
- Time: Time of day (optional)
The scheduler uses claude code cli to inteligently interpret and create various workout types: - Recovery runs: "8km recovery run at zone 1"
- Easy runs: "10km easy run at zone 2"
- Long runs: "30km long run at zone 2"
- Tempo runs: "3km warmup, 6km tempo at 162-168bpm, 3km cooldown"
- Intervals: "2km warmup, 6x(1km at 170bpm, 90sec recovery), 2km cooldown"
- Norwegian 4×4: "3km warmup, 4x(4min at 175-180bpm, 3min recovery), 2km cooldown"
- Zones: "zone 1", "zone 2", etc.
- Ranges: "162-168bpm"
- Limits: "under 125bpm", "below 135bpm"
models.py : Pydantic models for training datautils.py : Markdown parsing and workout conversionschedule_training_plan.py : Main CLI implementation
The scheduler is idempotent, meaning you can run it multiple times safely: - Checks if a workout already exists on each date
- Compares existing workouts with planned workouts
- Only schedules new workouts or replaces non-matching ones
- Skips scheduling if the existing workout matches
After scheduling, the tool validates all workouts: - Verifies each training date has a scheduled workout
- Checks that workouts can be retrieved from Garmin
- Reports any missing or invalid workouts
- Provides a validation summary at the end
- Validates markdown format before processing
- Handles authentication failures gracefully
- Retries failed operations up to 3 times
- Provides clear error messages
╭──────────────────────────────────────────────────────────────────────────────╮
│ Marathon Training Plan Scheduler │
│ │
│ Reading: training_plan.md │
╰──────────────────────────────────────────────────────────────────────────────╯
Training Plan Summary
┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Property ┃ Value ┃
┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Title │ Marathon Training Plan │
│ Start Date │ 2025-07-21 │
│ End Date │ 2025-09-28 │
│ Total Weeks │ 10 │
│ Total Sessions │ 70 │
└────────────────┴─────────────────────────────────────────────────────────────┘
Scheduling workouts...
✓ Scheduled 2025-07-21: Recovery Run
⟳ Exists 2025-07-22: Tempo Run (matched)
✓ Scheduled 2025-07-23: Easy Aerobic
...
Scheduling Summary:
✓ Newly scheduled: 45
⟳ Already existed (matched): 25
○ Rest days: 2
✗ Failed: 0
Validating scheduled workouts...
✓ Validated 2025-07-21: Recovery Run
✓ Validated 2025-07-22: Tempo Run
...
Validation Summary:
✓ Validated: 70
✗ Validation failed: 0
Example session uploaded to Garmin ConnectHere' the first session of my plan uploaded to Garmin Connect:  |