runcoach
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| RUNCOACH_TZ | No | IANA zone for 'which day was this run' | system zone |
| RUNCOACH_LOG | No | Log level — set INFO or DEBUG to watch a sync. INFO and DEBUG also log every HTTP request of the web app | WARNING |
| RUNCOACH_HOME | No | Data directory | ~/.runcoach |
| RUNCOACH_MODEL | No | Model for coach cards | CLI default |
| RUNCOACH_TOKEN | No | Required for --host 0.0.0.0 (phone in your home network) | |
| RUNCOACH_QUOTA_WAIT_S | No | How long a job waits for subscription quota | 3600 |
| RUNCOACH_GARMIN_TOKENS | No | Reuse an existing python-garminconnect token dir | ~/.runcoach/garmin |
| RUNCOACH_JOB_TIMEOUT_S | No | Hard timeout for a single coach job | 600 |
| RUNCOACH_LT_HISTORY_DAYS | No | How far back the lactate-threshold history is fetched | 180 |
| RUNCOACH_ACTIVITY_BACKFILL_DAYS | No | Minimum window of workouts a sync fetches (the ACWR fallback needs ~28 days) | 35 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| get_coaching_referenceA | The coach's full rule set: how to read the data, today's decision, the planning rules, zones and intensity distribution, the energy boundary and the sources behind each threshold - the same text the app's own coach works from. Call it ONCE per conversation, before the first training advice, and follow it. No arguments, no Garmin call; it reads nothing of the athlete. |
| get_training_readinessA | Today's readiness verdict GO / EASY / REST with the signals behind it (HRV status, sleep score, Body Battery, resting HR vs 27-day baseline, ACWR, days since the last hard workout). Rule-based and conservative. START HERE for "should I train today?". Flags stale data explicitly. When the last Garmin sync is older than 30 minutes it syncs first (10-40 s) and says so on top. |
| get_recovery_summaryA | Averages of sleep, HRV, resting HR, stress, Body Battery and steps over the last N days plus a snapshot of the latest day. Use for "how has my recovery been?". Aggregates only — never a day-by-day list. |
| get_daily_metricsA | Every recorded recovery value for ONE day (default: latest day with data). Use when a single night/day is in question, not for trends. |
| get_trendA | Weekly averages of ONE recovery metric over the last N days — for "is my HRV / resting HR / sleep trending up or down?". |
| get_training_loadA | Training-load picture: ACWR with its SOURCE (Garmin's EWMA ratio, or a self-computed fallback that is less reliable), Garmin training status, VO2max with change, and weekly load buckets. VO2max change is only reported when the value actually varied (Garmin carries the last value forward). |
| get_recent_activitiesA | Workout digest: totals per sport plus the latest workouts, each run classified Quality / Long Run / Easy (from training effect and duration). Use to see what was actually trained before recommending the next session. |
| get_intensity_distributionA | Time in heart-rate zones across all runs (easy Z1-2 / moderate Z3 / hard Z4-5 / Z5) with percentages — the basis for the 80/20 polarisation question. States how many runs have zone detail, so an incomplete picture is visible. |
| analyze_workoutA | Deep dive into ONE run: time in each HR zone, interval structure (rep count, rep length, work vs recovery HR), weather, performance condition, plus rule-based notes. Default: the latest run with detail. The interval structure is Garmin's auto-detection — if the athlete states a different structure, the athlete is right. |
| get_vo2max_historyA | VO2max over the last 8 weeks as STEPS (only the days the value really changed — Garmin carries it forward in between) plus a descriptive comparison of the last 28 days with the 28 before (distance, Z5 minutes, easy share, temperature). Use for "why is my VO2max moving?". Descriptive, not causal. |
| sync_garminA | Pull the latest days and workouts from Garmin Connect NOW. Call this first when today's run or last night's sleep is missing. Takes 10-40 s. Read-only towards Garmin; writes only to the local database. |
| propose_workoutA | Build a structured session for the athlete's route or time budget and file
it as a PROPOSAL - warm-up, work reps with targets, recovery jogs, cool-down -
sized so the whole thing adds up to the route. Targets come from the
athlete's own Garmin zones; anything not measured is listed as an assumption.
Use when the athlete asks for a workout ("plan me intervals for my 10 km",
"an easy 45 minutes", "a threshold session") or when the readiness verdict
calls for a different session than the one on the calendar.
Kinds: easy/long (one capped step), threshold (Z4 reps), vo2max (Z5 reps),
steady (>= 12 min even effort at threshold - the only shape Garmin measures
VO2max from). |
| propose_weekA | Build a polarised training week and file it as ONE proposal: a VO2max
session and a threshold session 48 h apart, the long run on its weekday,
easy runs between, nothing hard the day before or after the long run, at
least one rest day. Sized from the athlete's last four weeks of running
(+10 %), targets from their own zones. Use when the athlete asks for their
week ("plan my week", "what should next week look like"). The calendar
wins: a day that already has an entry gets nothing, and a quality session
too close to a hard calendar day becomes an easy run - each change is a
|
| apply_workoutA | WRITE a proposed session to the athlete's Garmin account: upload the workout, schedule it on the proposal's day, push it to the watch, then read it back and verify structure and targets. Call ONLY after the athlete has seen the preview and explicitly agreed - a request ("plan me 10 km") is not agreement, "yes, put it on the watch" is. Not available to the app's own card runs; there the athlete clicks. Returns what is now on Garmin, with any warning (not pushed because the watch is offline; a mismatch found on read-back). A proposal can be applied once; an unknown or used id says so and lists the open ones. |
| undo_workoutA | Take a session (or a whole applied week) back OFF the athlete's Garmin calendar. The counterpart of apply_workout: use it when the athlete says the session should not be there after all, or after trying the write path out. It unschedules - the workout stays in their Garmin library, so applying the same proposal again puts it back on a day without building or uploading anything a second time. Only sessions this app uploaded and scheduled are touched; a calendar entry that has changed hands since is left alone and reported. Like apply_workout it needs the athlete's explicit yes, and it is not available to the app's own card runs. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 15 tools
Most tools have sharply distinct purposes, and the descriptions actively steer selection (e.g. get_recovery_summary is 'aggregates only', get_daily_metrics is 'one day', get_trend is 'one metric over time'). The main residual overlap is between get_recovery_summary, get_trend and get_vo2max_history, all of which can answer 'how is my X going?', and between the three load-related tools, though the boundaries are documented. The propose/apply/undo trio is clearly delineated by 'nothing is written' vs 'only after explicit yes' vs its counterpart.
Uniform snake_case verb_noun convention throughout: get_*, propose_*, apply_*, undo_*, sync_*, analyze_*. Verbs are meaningful and consistent with the action's semantics (propose vs apply vs undo clearly encode lifecycle stage).
15 tools sit at the top of the well-scoped range and each maps to a distinct stage of the coach loop: read data, sync, plan, apply, undo, plus the rule-set reference. No filler tools and no duplicated capability.
Strong end-to-end coverage of the training-coaching lifecycle: data ingest, analysis, planning, write-back and rollback. Minor gaps: no tool to inspect the existing Garmin calendar or list open proposals, even though propose_week's 'the calendar wins' logic depends on that data, and no explicit accessor for the athlete's zones/profile that targeting relies on.