Skip to main content
Glama

TriSync

Plan it. Sync it. Train.

A local MCP server that turns training plans into Garmin Connect workouts, schedules them, and sends them to the right device. An optional Codex skill coordinates a timed Google Calendar event with a workout summary.

What it does

  • Running, cycling and pool-swimming workouts with warm-ups, repeats, recovery, rests and cool-downs.

  • Distance, time and lap-button steps; running/cycling pace, heart-rate and power targets.

  • Checks Garmin's live library and calendar before creating matching entries, including when the local ledger is missing.

  • Scheduled runs/swims queue to a Forerunner 255; scheduled cycling workouts queue to an Edge 530. These are the current built-in defaults; edit DEVICE_MODELS in garmin_mcp.py to adapt them.

  • A reusable garmin-workout-calendar skill asks when you intend to train, checks for an existing Google Calendar event, and creates a timed event containing the workout summary and Garmin link.

Device transfers are queued through Garmin; the device must sync to receive them. Google Calendar coordination is performed by the assistant through its connected Google Calendar plugin, not by a Google API client embedded in this server.

Related MCP server: garmin-mcp

Requirements

  • Windows and Python 3.12+.

  • A Garmin Connect account with the target devices registered.

  • An MCP-compatible desktop client for chat-driven operation.

  • A connected Google Calendar plugin if using the calendar skill.

This project uses the unofficial python-garminconnect client. Garmin's private endpoints can change. It is not affiliated with Garmin. Workout compatibility depends on the sport and device.

Install

From the repository folder in PowerShell:

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Copy-Item .env.example .env

Edit .env locally and set GARMIN_EMAIL and GARMIN_PWD. Never paste those values into chat. The server reads this file with password interpolation disabled; process environment values take precedence. Credentials and authentication tokens are never included in tool results. Cached tokens live under %LOCALAPPDATA%\GarminWorkoutTool\tokens. Keep this project outside cloud-synced folders if you do not want .env synchronized.

No PowerShell activation script is needed: invoke the virtual environment's Python executable directly. If Garmin requires MFA, run the manual login once:

.\.venv\Scripts\python.exe garmin_workouts.py login

Connect the MCP server

For Codex, replace both paths below with your absolute paths:

codex mcp add garmin-workouts -- "C:\path\to\trisync\.venv\Scripts\python.exe" "C:\path\to\trisync\garmin_mcp.py"

Reload the client to discover the tools. Other MCP clients can start the same Python script over stdio. The process waits for MCP requests; there is no HTTP port or separately managed background service.

Tools:

Tool

Purpose

garmin_preview_workouts

Validate and preview plans offline

garmin_connection_status

Verify Garmin sign-in

garmin_workout_devices

Verify exact sport/device routes

garmin_list_workouts

Read paged workout names and IDs

garmin_get_workout

Inspect workout steps

garmin_add_workouts

Reuse/create, schedule, and queue dated workouts

Example request:

Add a swimming workout for tomorrow: 200 m warm-up, 4 × 50 m technique, 6 × 200 m easy with 25 seconds rest, 200 m cool-down. Pool length: 25 m.

The assistant converts your instructions to structured JSON; this tool does not parse arbitrary prose or PDFs by itself.

Optional calendar skill

Copy skills/garmin-workout-calendar to your personal Codex skills folder, normally ~/.codex/skills/. Reload Codex and connect Google Calendar. The skill can be selected automatically for Garmin workout requests or invoked as $garmin-workout-calendar.

The included skill defaults to Europe/Amsterdam, a 25 m pool and the device mapping above. Customize those preferences for your setup. It asks for missing date/time/duration, searches all event pages for that local day, reuses matching events, and asks before resolving ambiguous matches or time conflicts. Events include the workout steps and a Garmin ID marker to make later duplicate checks more reliable. This is an assistant workflow, not an unattended Google Calendar integration.

Manual JSON workflow

.\.venv\Scripts\python.exe garmin_workouts.py preview workouts\swimming-example.json
.\.venv\Scripts\python.exe garmin_workouts.py export workouts\swimming-example.json
.\.venv\Scripts\python.exe garmin_workouts.py upload workouts\swimming-example.json

Examples are illustrative, not personalized training prescriptions. Change the date before scheduling them. Manual CLI uploads retain ledger-based duplicate protection; the live duplicate checks and device delivery are provided by the MCP add tool.

{
  "name": "Easy running intervals",
  "sport": "running",
  "date": "2030-01-10",
  "steps": [
    {"kind": "warmup", "seconds": 600},
    {"repeat": 4, "steps": [
      {"meters": 1000, "target": {"pace": ["5:15", "5:30"]}},
      {"kind": "recovery", "seconds": 120}
    ]},
    {"kind": "cooldown", "lap": true}
  ]
}

date is optional; without it the workout stays in the library and is not sent to a device. Swimming requires pool_length_m and whole-pool-length distances. Use notes for drills and effort instructions. Targets for running/cycling support one of hr_zone, bpm, pace (min:sec/km), or watts. Batch input is {"workouts": [...]}; the MCP tools take the workout array directly.

Duplicate handling and recovery

The MCP server reads the live Garmin library, compares matching names/sports/steps, and checks the requested date before writing. It reuses an exact existing workout and prefers a copy already scheduled on that date. Same-name workouts with different content are distinct. A failed lookup stops the write. The library scan is capped at 10,000 entries and refuses unchecked creation at that limit.

The local ledger is garmin-data/workout-upload-state.json. It tracks workout IDs, scheduled dates and per-device sends. Keep it between runs. One Garmin account per project/ledger; don't run the legacy CLI concurrently with MCP writes.

An upload, schedule or device-send request may succeed even if its response is lost. Such writes are marked pending/uncertain and are not blindly retried. Check Connect first. Definite matching remote workouts/schedules can be reconciled on the next call. For unresolved device delivery, inspect Garmin's device queue before changing the corresponding device_pushes record. Never delete the ledger merely to force a retry. Batch operations can partially succeed; report Garmin creation, scheduling, device queueing and Google Calendar creation separately.

Tests

Offline unit tests require no credentials and make no Garmin/Google writes:

.\.venv\Scripts\python.exe -m unittest -v test_garmin_workouts.py test_garmin_mcp.py
.\.venv\Scripts\python.exe test_garmin_mcp.py --protocol

--live is an explicit, read-only integration check that signs in to Garmin, checks registered devices and searches for the example workout. It uses an isolated local ledger. Actual device-send writes are covered with mocks, not a live transfer test.

License

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to read Garmin activities and create/schedule structured workouts and multi-week training plans on Garmin Connect, syncing to the user's watch.
    1
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Enables triathlon coaches and athletes to interact with Garmin Connect, including retrieving health/activity data, building and uploading structured workouts (cycling, running, swimming, brick), and accessing coaching analytics like readiness, load, and performance trends.
    172
    MIT