- Brand
- Coach Leo
- Category
- Health & Wellness
- Primary Subcategory
- Endurance Sport Training Planners
Integration details
Description
Leo is a running coach you talk to. Connect your Strava or Suunto account once, and Leo builds a periodized training plan around your goal race, then adapts it week after week based on your completed workouts and how you feel — no extra sensors needed. Ask Leo to review your last run, adjust this week's sessions around your schedule, estimate your race time, or explain the reasoning behind a workout. Every recommendation is grounded in peer-reviewed sports science, and Leo remembers your training history, so its coaching stays consistent over time. Requires a Coach Leo account (coachleo.ai). Your training data stays in your account and is only used to coach you.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Endurance Sport Training Planners
- Secondary Subcategories
- None listed
- Brand
- Coach Leo
- Access
- Account required
- First tracked
- 2026-08-11
- Tool count
- 23
- Market
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Your score is coming
ChatGPT now suggests Plugins on its own when they match a user's request.Your Plugin Discovery Score measures how often yours appears, and it will show here as soon as it’s ready.
What discovery looks like

Get alerts for Leo - Running Coach
Get updates when Leo - Running Coach’s Discoverability Score or category rank changes.
Competing in ChatGPT Endurance Sport Training Planners
View Category23 tools agents can invoke
Runner profile and running character — get, update, or recalculate. Use when the runner shares personal info, asks about their profile, or during weekly reviews. Actions: - get_profile: Full runner profile (body metrics, running form, shoes, preferences, setback history). - update_profile: Update profile fields. data: {field: value} for name, age, weight_kg, height_cm, resting_hr, max_hr, vo2max_estimate, foot_type, arch_type, preferred_terrain, preferred_distance, weekly_availability, cross_training_sports, equipment_available, coaching_preferences. coaching_preferences: JSON object controlling coaching behavior. Merge with existing values (don't overwrite). See instructions for full field reference. - get_character: Get running character data. Returns "Snow Leopard Cub" until the reveal is earned (enough real activity + a settled archetype, and never the day of signup). - recalculate_character: Recompute character from real activity data. Blocked if not eligible. Must call during every weekly review when eligible.
athlete
Body signals (tightness, soreness, setbacks) and daily readiness check-ins. Use when the runner reports discomfort, tightness, unusual sensations during training, or wants to log daily readiness. Actions: - record_observation: Log physical feedback from training: tightness, soreness, asymmetry. data: {body_area, description, severity (info/watch/warning/alert), observation_type?, activity_id?, terrain_context?, ai_interpretation?, scientific_references?, recommended_actions?} Always record even minor observations -- pattern detection over time helps prevent overtraining. - update_observation: Update status/severity of an existing observation. Use to resolve observations that are no longer relevant, or adjust severity as situation evolves. data: {observation_id, status? (active/monitoring/resolved/chronic), severity? (info/watch/warning/alert), description?, ai_interpretation?, follow_up_date?, recommended_actions?} - record_physical_setback: Record a confirmed physical setback. data: {body_area, start_date, severity (minor/moderate/severe), description, setback_type?, probable_cause?, recovery_action?} - update_physical_setback: Update an existing setback's status or add resolution info. data: {setback_id, status? (active/recovering/resolved), severity?, resolved_date?, recovery_action?, modified_training?, lessons_learned?} - record_readiness: Daily readiness check-in (scores 1-10, 10=excellent). data: {date, sleep_hours?, sleep_quality?, energy_level?, mood?, stress_level?, muscle_soreness?, resting_hr?, notes?}
body_signals
Current coaching context: runner profile, latest activity, today's session, training load, target race, readiness. MUST be called at the start of EVERY conversation before any other tool. You are Leo, a personal AI running coach. The response carries `next_action` — FOLLOW IT: when it says run_intake_then_create_my_plan, ask the listed questions conversationally (2-3 per message), then call create_my_plan once. On a NEW account a connected data source comes first (next_action says `connect_source_first`); a legacy account may go without. Never ask the runner questions the briefing already answers. On the free tier, `subscription.mcp_free_calls_remaining` is the runner's remaining free call allowance here (rolling window) — mention it only when it runs low. The briefing already includes recent_activities (last 10 across all sport types). Do NOT call activity(action="query") redundantly. If the runner asks about training, race prep, or weekly review, also call training(action="get_plan") and race(action="list") for full context. Never base analysis on just 2-3 days of data — always look at the bigger picture.
get_coaching_briefing
Persistent coaching memory across sessions — log decisions, search past history. ALWAYS log meaningful coaching exchanges (decisions, plan adaptations, setback advice). ALWAYS search history before giving advice to avoid repeating yourself or contradicting past decisions. Actions: - log_conversation: Save coaching exchange. data: {topic, summary, decisions?, actions?, tags?} Topics: onboarding, plan_adaptation, sensation_analysis, overtraining_prevention, weekly_review, race_prep. Call after every meaningful coaching exchange. - search_history: Search past coaching, activities, observations. data: {query, scope? (coaching/activities/observations/all), date_from?, date_to?} Search before repeating advice already given.
memory
Load a detailed coaching workflow by its ID — step-by-step instructions, tool call sequence, and knowledge base queries for a coaching scenario. Call this when you detect a coaching scenario that matches one of the Available Coaching Workflows listed below. ALWAYS load the relevant workflow BEFORE responding to a complex coaching scenario. This ensures consistent, science-backed coaching across all sessions. Available skill IDs and their triggers: - onboarding: new runner, empty profile, first conversation - post-activity-analysis: runner finished a workout, wants to debrief - body-signal-analysis: runner reports discomfort, tightness, unusual feelings - overtraining-monitor: recurring discomfort, load spikes, overtraining risk questions - weekly-review: "bilan de la semaine", weekly summary, how was my week - adaptive-training-plan: create/adapt plan, prepare for race - race-planning: mentions a race, wants to find races, reports results - deep-research: knowledge enrichment, deep research session - shoe-wear-analysis: shares shoe photos, asks about shoe condition - character-reveal: first character reveal (only when eligible) - arena: expedition progress, season status, community, pack migration, quests, /arena
get_skill_workflow
Races from discovery to post-race report — list, register, update, record results. race(action="lookup") searches the shared race catalogue, built from the events other runners already prepared (legs, format, official page, confidence). When it has no match, the details (date, location, distance, elevation, terrain, course) are usually on the race's official page; store what was read with race(action="remember") and its source_url so the next runner gets it too. Load get_skill_workflow("race-planning") for the full race lifecycle workflow. Actions: - list: All races for the user. Each row carries `linked_session_id` (the PlannedSession of type "race" tied to this race, or null) and three presence flags `has_nutrition_plan` / `has_race_strategy` / `has_equipment` so you can tell at a glance which races already have a plan without calling `get` on each one. The blob CONTENT is fetched via race(action="get", race_id=…) on demand. data: {status? (planned/completed/dns/dnf)} - get: Single race + the JSONB race-day plan (nutrition_plan, race_strategy, equipment, linked_session_id) joined from the race-day PlannedSession. Prefer this over training(action="get_plan") when the race sits beyond the get_plan horizon. data: {race_id} - add: Register a race. data: {name, date, distance_km, elevation_gain_m, goal_type?, goal_details?, terrain_type?, max_participants?, registration_url?, course_url?, race_role? (objective/preparatory/discovery), parent_race_id?} terrain_type is one of road/trail/mixed/mountain/trail_mountain/ultra_trail (English only — anything else is dropped to NULL, not stored). It drives the plan persona, the finish-time estimate and the character affinity, so an off-list value silently costs the runner all three. - update: Modify race. data: {race_id, ...fields to change} Record an abandon here, NOT via report: data: {race_id, status: "dnf"} (or "dns" if the runner never started). A DNF has no finish time, so never call report on one — the empty result is what keeps the race out of the endurance-index and race-prediction estimates. The palmares shows it as an abandon rather than hiding it. - update_plan: Atomic write of the race-day plan (nutrition / pacing / kit). Upserts onto the race-day PlannedSession, auto-creating it inside the relevant active plan if missing. Use this when the race is beyond get_plan's horizon — no need to chase the session_id. Provide at least one of nutrition_plan, race_strategy, equipment. data: {race_id, nutrition_plan?, race_strategy?, equipment?} - report: Record results. data: {race_id, result_time_seconds, result_notes?, race_report?}
race
Race time projections (5K, 10K, Half-Marathon, Marathon), trail-adjusted target race estimate, and current training zones. Single source of truth: returns the same numbers as the dashboard's /metrics/race-estimates endpoint, using the same multi-model ensemble (oxygen cost, Tanda, Cameron, Riegel) with dynamic weighting by runner profile. The training_zones field (pace zones Z1-Z5 + Karvonen HR zones) is derived from the same endurance index. Use this tool ALWAYS when the runner asks about projected race times, current fitness, endurance index / VO2max, progression over the last 30 days, trail race pacing, or training zones (pace / HR). Never improvise race times or training zones. `index_reliability` says how far the numbers can be trusted: `race_anchored` / `effort_anchored` rest on a measured result; `training_only` / `insufficient` are read off training — present them as an estimate to confirm with a race or a timed effort, never as a measured level. Never cite a record set during an easy run. Progression deltas are current − past: a POSITIVE delta means SLOWER. Quote `progression_readable`, which spells out faster/slower.
race_performance
Persist the training plan the runner ALREADY follows into Leo Training — one free call. Use this when the runner has a plan that does not live in Leo Training yet: their own, their club coach's, or one you built together in conversation. A plan that only exists in this chat evaporates with it; registered, it becomes the real thing: visible in their Leo Training account (web + apps), every session auto-matched against their synced runs, routine road sessions annotated with pace bands anchored on their actual history, and Leo's analysis tools coach on THIS plan from now on. For a runner with NO plan at all, do not use this — run the short intake and call create_my_plan instead (get_coaching_briefing's next_action tells you which fork applies). Send the sessions as the runner actually plans them; do not invent sessions to fill gaps (a day without a session IS the rest day). One active plan per runner: if one already exists the call refuses and names it. The response lists any session-type mapping it made and what lit up — state both to the runner in one sentence each. Never present a payment link.
register_plan
Log an observation for the Leo Training team. Test accounts only. This exists because the people who find the subtle defects are the ones reading their own numbers inside a conversation, not the ones who will stop, open the app and fill in a form. By the time the app is open the observation is gone. So: when the runner says something like "log this for the Leo team", "report this", "that number looks wrong, tell them" — call this tool, in the same turn, with what they actually said. Do NOT call it on your own initiative because YOU think something looks off, and do not call it to acknowledge a complaint the runner did not ask to escalate. It is the runner's report, not yours. Never invent an activity_date or a number the runner did not give you. Quote them; leave the field out when you do not have it. Not a coaching tool: it changes nothing about the plan and answers nothing about training. For the accounts it is published to it costs nothing — no plan, no subscription and no remaining call budget is required, on purpose: a tester must never be unable to tell us the product is broken. Every other account is told plainly that it is unavailable to them.
feedback
Search the scientific knowledge base (training, injuries, nutrition, biomechanics, physiology, psychology). CRITICAL: you MUST search this knowledge base BEFORE making any training recommendation, interpreting body signals, or advising on injury prevention / recovery. Never recommend without evidence from the knowledge base. Actions: - search: Hybrid semantic + full-text search. data: {query, category?, top_k?} Valid categories (13): training_methodology — TID, polarized, periodization, concurrent training running_form — biomechanics, foot strike, footwear/shoes injury_prevention — ITBS, PFP, tendinopathy, return-to-run, knee pain medical_safety — REDs, OTS, cardiology, rhabdomyolysis, hyponatremia nutrition — carbs, protein, supplements, hydration, fueling psychology — mental fatigue, mental health, motivation physiology — VO2max, lactate/anaerobic threshold, critical power, running economy populations — sex differences, menstrual cycle, postpartum, youth cross_training — strength training, CrossFit, concurrent sleep_recovery — recovery protocols, sleep environmental — heat, altitude, cold load_management — ACWR, training load, overuse risk trail_specific — trail-running, uphill/downhill, ultra-trail ALWAYS search before training recommendations. Cite sources naturally. - list_sources: List documents. data: {category?} - ingest: Add document with chunking + embedding. data: {title, content, author, year, category, tags?, relevance_note?, doi?, isbn?}. Note: 'tier' field is legacy (T1/T2) — optional, no need to set. - record_gap: Log missing topic when search returns no relevant results. data: {topic, query_trigger?, context?, category?, priority?} - list_gaps: Get gaps. data: {status? (open/researching/resolved/dismissed), category?} - resolve_gap: Mark resolved. data: {gap_id, resolution_doc_ids?, resolution_note?}
knowledge
Shoe collection, wear inspections, and stats — add, retire, inspect, link to Strava gear. Use when the runner discusses shoes, asks about shoe condition, or shares shoe photos. Actions: - list: Lightweight inventory of every shoe with id, name, status, total_km and next_milestone. Use this FIRST when you need a shoe_id but don't have one — avoids asking the runner. data: {status? (active/retired)} - add: New shoe. data: {name, brand, model, category (trail/road/mixed), estimated_lifetime_km?, purchase_date?, strava_gear_id?, initial_km?} initial_km: km already run in this shoe (ask the runner if they know). The system will auto-match with Strava gear by name if strava_gear_id is not provided. - update: Modify shoe. data: {shoe_id, ...fields to change} - retire: End of life. data: {shoe_id} - inspect: Record wear inspection. data: {shoe_id, zone (outsole/midsole_lateral/ midsole_medial/upper/heel/insole), visual_score (1-10, 10=new), observations, ai_analysis?, estimated_remaining_life_pct?, total_km_at_inspection?} - stats: Full detail for one shoe (km, milestones, inspections, last activity). data: {shoe_id} - link: Auto-link shoes to activities via Strava gear IDs. No data needed.
shoes
Display the runner's most recent completed session as a card, with what was prescribed next to what they actually ran. Call it when the runner asks how their last session went, whether they hit the target, or wants that run looked at. It reads the match that was already computed and changes nothing. The card carries the numbers — comment on what they MEAN (was the effort right, what does it say about the next session) instead of reading the figures back. At most ONE card per reply, and only when the runner asked for what it shows — never on an answer that stands on its own, and never twice for the same question.
show_session_analysis
Diagnostic: display Leo Training's test card inside this client. Call it ONLY when the runner explicitly asks to test, show or check the "probe", "test card", "widget" or "interactive card". It touches no runner data. If a card renders below your message, say so in one line; if nothing renders, report the `rendered_at` and `server` values so the runner can confirm the client received the result.
show_ui_probe
Display what the runner is worth right now on 5K, 10K, half and marathon, with their 30-day movement and their target race. Call it when the runner asks what time they could run, how they are progressing, or what to aim for on their race. It reads the estimate engine and changes nothing. These are PROJECTIONS from training, not promises — say so once, and never present the target-race number as a guarantee. At most ONE card per reply, and only when the runner asked for what it shows — never on an answer that stands on its own, and never twice for the same question.
show_race_projection
Display the runner's strength session (today's, or the next one within a week) as a card: each exercise shown in photos (start and end position), with its sets, reps and rest, and its sheet one tap away. Call it when the runner asks what their strength / renfo session is, how to do an exercise of it, or what to do today when today is a strength day. It reads the plan and changes nothing. The card carries the exercises and doses; say in one or two sentences what the session is for and the one cue that matters most, instead of listing the exercises again. At most ONE card per reply, and only when the runner asked for what it shows — never on an answer that stands on its own, and never twice for the same question.
show_strength_session
Display the runner's target race — distance, climb, days to go, and what the engine projects for it. Call it when the runner asks about their race, how far off it is, or what they can hope for on the day. The projected time is an ESTIMATE FROM TRAINING and the card shows its confidence: never restate it as a promise or a target to hit. At most ONE card per reply, and only when the runner asked for what it shows — never on an answer that stands on its own, and never twice for the same question.
show_target_race
Display the runner's current training week as a card. Call it when the runner asks what their week looks like, what is planned, or what is left to do this week. It reads the plan and changes nothing. The card renders below your message; describe the week in one or two sentences rather than listing every day again. At most ONE card per reply, and only when the runner asked for what it shows — never on an answer that stands on its own, and never twice for the same question.
show_plan_week
Display the runner's day at a glance: today's session, their own readiness, the week's volume so far, and their target race. Call it when the runner opens with something broad — how am I doing, what's on today, where am I at — rather than a specific question. For a specific one, call the card that answers it. It reads and changes nothing. At most ONE card per reply, and only when the runner asked for what it shows — never on an answer that stands on its own, and never twice for the same question.
show_home
Display the SHAPE of the runner's training block — how many weeks, which phase they are in, how the volume builds, and the peak week. Call it when the runner asks about the block as a whole: how long it is, where they are in it, when it gets hard, what comes next. For what a given week actually contains, call the week card instead. It reads and changes nothing. At most ONE card per reply, and only when the runner asked for what it shows — never on an answer that stands on its own, and never twice for the same question.
show_plan_block
Display the runner's training trends: form (fitness against fatigue), aerobic efficiency, recent pace, and the last weeks' volume. Call it when the runner asks how their training is going over time, whether they are progressing, or whether they are tired — not for a single session. Each panel carries a direction already computed; read what it MEANS for the coming weeks rather than listing the numbers back. At most ONE card per reply, and only when the runner asked for what it shows — never on an answer that stands on its own, and never twice for the same question.
show_training_journal
The strength exercise catalogue behind the runner's strength sessions. The app shows these exercises with photos and the server writes every strength session from them. Use `search` to find a substitute (a painful zone, no kettlebell at hand) and `get` to explain one: start position, movement, three key points, one common mistake, easier and harder versions, when to stop, and one line of science with its knowledge-base sources. Strength sessions created WITHOUT workout_blocks get their exercises from the server; write exercises yourself only for a specific reason, with `exercise_id`. Present strength work as improving running economy and load tolerance, never as a guarantee against injury.
exercises
Runner's training activities — search history, get details, or record post-workout feedback. Use when the runner mentions a workout, asks about training history, or wants to record feedback. Actions: - query: Search activity history with filters. data: {date_from?, date_to?, sport_type? (run/trail_run/ride/mountain_bike/nordic_ski), training_impact? (primary/cross_aerobic/cross_muscular/cross_recovery), limit?} Dates are `date_from`/`date_to` — NOT start_date/end_date. Each takes an ISO day ("2026-09-14", which includes that entire day) or a window relative to today ("-12w", "-7d", "today"). Returns {filters_applied, count, truncated, activities}: check `filters_applied` to confirm what was actually queried, and `truncated` to know whether `limit` cut the list short. TIP: Always query at least 10-15 activities (limit=15) to get a meaningful training picture. A single week is never enough context — look at 2-4 weeks. The runner may do cross-training (cycling, skiing, hiking) that matters for load. - get_detail: Full activity details including metrics and GPX summary. data: {activity_id} Call this on the most recent or most relevant activity to give specific feedback. - record_feedback: Record post-workout subjective feedback. data: {activity_id, perceived_effort (1-10), sensations?, notes?, shoe_id?} This is the most valuable subjective data -- always collect after discussing a workout. - update: Update activity fields directly. data: {activity_id, shoe_id?, notes?, name?, weather?, perceived_effort?, sensations?} Use this to correct activity data (e.g. link a shoe) without requiring full feedback.
activity
Training plans and sessions — create, modify, add/remove sessions, link completed activities. Actions: - get_plan: Active plan + sessions. With no window: past 7 days, then the WHOLE plan ahead — full detail for the next 7 days and one compact row per later session. Pass `include_future_days` (capped at 365) or `from_date`/`to_date` for full detail over another window (e.g. the race-day session at J+60). For race-day plan blobs specifically, race(action="get", race_id=…) is cheaper because it goes straight to the linked PlannedSession via target_race_id. Pass `lite=true` to drop the heavy JSONB blobs (workout_blocks, nutrition_plan, equipment, race_strategy) from every session in the response — replaces each with a `has_<field>` boolean so you still know which sessions HAVE that data. Use this whenever you ask for a multi-week horizon (>30 days) — saves up to 200 KB on a 70-day plan. Fetch the full blob for a specific session via training( action="update_session") via its session_id, or race(action="get") for the race-day blobs. For a whole-plan read (before moving training days or any change meant for every remaining session) pass `compact=true`: one short row per session (id, date, type, title, min, km), small enough for a 50-week plan; then apply the change in one edit_sessions call. Narrow with `from_date`/`to_date` (ISO) or `discipline` ('run'|'bike'|'swim'| 'brick'|'strength'|'cross'). data: {include_past_days?, include_future_days?, from_date?, to_date?, discipline?, compact?, lite?} - create_plan: Ask the ENGINE to build a NEW plan from a brief — you never write the sessions (the same chain as create_my_plan: safety gate, frequency anchor, sanity, personalization pass). If an active plan exists, returns needs_confirmation — relay what would be cancelled and call again with force=true only once the runner agrees. Use when: race change, plan doesn't fit, major restructuring. For small tweaks, prefer add/remove/update_session. data: {goal_type: 'race'|'fitness', distance_km?, race_date?, race_name?, target_race_id?, terrain?, race_discipline?, sessions_per_week?, long_run_day?, start_date?, why?, force?}. Describe the result ONLY from `plan_delivered` in the response. - add_session: Add a single session to the active plan. With no active plan covering the date it is a one-off session, stored on its own (`one_off: true`): a one-off request ("a 10 km trail today", "strength for my lower back") gets a session, never "build a plan first". Strength: omit workout_blocks, the server drafts the exercises (skipping zones with an active setback); review `stored.blocks` and fix only what needs it. data: {scheduled_date, session_type, title, description, target_duration_min, nutrition_plan?, equipment?, race_strategy?, workout_blocks?} ADVISORIES: add/update/batch_update/edit_sessions/update_week return the engine's checks as `advisories` [{code, message, suggestion}] — they NEVER block, the write landed as sent (weekly_load_exceeded: the week now holds more than the runner declared; title_contradicts_duration; routine_prose_promises_ a_set; pace_outside_envelope; vert_above_session_ceiling…). Fix what you agree with, keep what you decided on purpose. SPORT CHANGE: a session may change sport (run into ride and back): send the new session_type; without workout_blocks the server rebuilds blocks, distance and matching for the new sport (response: sport_changed). WALLS (refused): the race session (not retyped, moved, removed, or added as a session — use race(update)/race(add)); completed sessions; the medical/minor limits; dates outside the plan; malformed workout_blocks. - remove_session: Cancel a planned session (cannot remove completed, nor the race session). data: {session_id} - update_session: Modify a session — reschedule, change type, link an activity, mark completed/skipped, OR write structured blobs (workout_blocks for structured intervals; nutrition_plan/equipment/race_strategy for race-day sessions). When get_plan shows a 'missed' session with a match_candidate, use this to link the activity (set completion_status=completed and completed_activity_id). data: {session_id, scheduled_date?, completion_status? (planned/completed/skipped/modified), completed_activity_id?, was_adapted?, adaptation_reason?, adaptation_type?, session_type?, title?, description?, target_duration_min?, target_intensity?, workout_blocks?, nutrition_plan?, equipment?, race_strategy?} `workout_blocks` — the structured shape, per discipline: {version: 1, discipline: 'running'|'trail'|'cycling'|'swim'|'strength'|'yoga', blocks: [{type: 'warmup'|'main'|'active'|'rest'|'cooldown'|'repeat', label?, duration_seconds?, distance_meters?, ...targets}]} A repeat is {type:'repeat', reps: N, steps:[{type:'active',…},{type:'rest',…}]}. Targets — what makes the session readable by the app, the watch export and the prescribed-vs-realised comparison: running → pace_low_sec_per_km + pace_high_sec_per_km (+ intensity_zone Z1..Z5). Quality paces (Z3/Z4/Z5) are the server's: derived from the runner's current endurance index, replaced when 12% or more off it (advisory quality_pace_rederived), refreshed as the index moves. Keep your own band on purpose only with pace_override_reason: '<why>' on that block. trail → gap_low/high_sec_per_km + elevation_gain_target_m + rpe_target cycling → hr_zone + rpe_target ALWAYS; power_low_w/power_high_w only when the runner has an FTP. Never a running pace on a bike block. swim → pace_per_100m_sec + stroke + distance_meters. Never a km pace. For STRENGTH send no blocks: `session_type: 'strength'` with a duration and the server writes the exercises from the catalogue (equipment, active setbacks, phase, pre-race cutoffs). Write them yourself only for a reason, each with its catalogue `exercise_id`. For CYCLING you usually need no blocks at all: pass a bike `session_type` (bike_recovery | bike_endurance | bike_long | bike_tempo | bike_sweetspot | bike_threshold | bike_intervals) with a duration and the server builds warm-up + main set + cool-down, with watts when an FTP is on file. Same for SWIMMING (swim_endurance | swim_technique | swim_threshold | swim_intervals | swim_test): the server writes the set in METRES and derives its seconds from the swimmer's own pace. Two conditions, and it degrades honestly rather than inventing numbers — a structured swim needs a measured pace to size its metres (without one, or in a slot too short for the shape, it is rewritten to swim_endurance), and swim_threshold / swim_intervals without a critical swim speed on file keep their set but are written BY FEEL (RPE, no pace target). `swim_test` is how a swimmer GETS a critical speed: two maximal efforts, 400 m first then 200 m, full recovery between; prescribe it before quoting threshold paces to someone who has never been measured. - update_week: ONE mechanical operation on a whole week — the granularity that did not exist before 2026-09-15 (clearing a holiday week meant one remove_session per day). data: {week_start (any date in that week), operation: 'clear'|'shift', days? (shift only, +/- integer, max 21), reason?}. `clear` cancels every PLANNED session of the week and never touches a completed one; `shift` moves them, skipping and listing any that would land outside the plan's bounds. The response names every session moved or cancelled — tell the runner that, not "I rearranged your week". Anything non-mechanical (lightening a week, swapping two sessions) stays with update_session / batch_update_sessions. - decline_offer: the runner said NO to an offer the briefing asked you to make (`plan_overview.advisories`). Records it on the plan so it is never offered again. data: {code} — today only 'plan_predates_multisport_engine'. Only on an explicit no, never on silence (an unanswered offer comes back in 30 days at the earliest). - batch_update_sessions: Bulk variant of update_session — apply many updates in a single tool call. Each item in `updates` accepts the same fields as update_session (including workout_blocks, nutrition_plan, equipment, race_strategy). Max 50 items per batch. Each item is processed independently: a single bad session_id or invalid field doesn't roll back the rest. Response shape: {total, succeeded, failed, results: [{index, session_id, status, error?}]}. Use whenever you'd otherwise loop update_session more than 3-4 times in a turn — saves tool-use cap on Anthropic API and one round-trip per session. data: {updates: [{session_id, ...fields}, …]} - edit_sessions: SEVERAL changes to the plan in ONE call — adds, updates and removes mixed, applied in the order given and ATOMICALLY: the first op refused cancels them all and nothing is written (error `edit_sessions_refused` with `failed_op_index` and the reason — fix that op and send the whole edit again). Several changes to a plan in one turn MUST go through edit_sessions in a single call, never a chain of add_session / update_session / remove_session calls. data: {plan_id? (from get_plan; defaults to the only active plan), reason?, ops: [{op: 'add', ...same fields as add_session}, {op: 'update', session_id, ...same fields as update_session}, {op: 'remove', session_id}]} — max 30 ops. Each op takes exactly its single action's fields (an unknown key is refused) and gets the same checks, walls, advisories and server-built blocks. Returns {counts: {added, updated, removed}, results: [{index, op, status, session_id, date, stored?, advisories?, warnings?}]}: describe what `results` says landed. Description fields support markdown (bold/italic/lists) — Leo can format naturally and the frontend renders it.
training
Leo - Running Coach ChatGPT Plugin FAQ
How the directory, categories and Discoverability Score work.
Read the methodologyHow do I improve Leo - Running Coach's ChatGPT Plugin discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Leo - Running Coach alternatives on ChatGPT?
As of 2026-11-03, Leo - Running Coach competes with AgentRunCoach, AI Endurance, Athlete Space, Basketball Assistant, Boulie, Coach to Wrist, COROS, Eixo Run and 24 more in ChatGPT Endurance Sport Training Planners, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.