← Registry

Productivity

watchout.run

An MCP server for generating and decoding shareable running workout and training plan links with calendar/Garmin/Apple integration and progress tracking.

1 endpoint22 known toolsFirst detected September 25, 2026Last detected September 25, 2026

ENDPOINT 1

https://watchout.run/mcp

No auth detected

MCP server metadata

Name
watchout
Version
1.56.0+d20b315
Capabilities
toolspromptsresources
Server instructions

WatchOut builds structured running workouts for Apple Watch and returns them as links — a real .workout file (Apple WorkoutKit) plus an optional calendar event. Workflow: ORIENT (every fresh conversation) — a returning runner's chat starts mid-plan, not at zero: never re-interview them or improvise a generic session just because YOUR context is empty. Fix three facts before answering. (a) NOW: today's date and the time of day — call the now tool (pass the profile's timezone for the runner's wall clock); read_workout_link results also carry the server clock (now.utc) and a when verdict (past/today/upcoming) per scheduled session; trust those over your own sense of date. (b) THE PLAN: your saved manifest/profile, else the runner's CALENDAR (host calendar tools: list the training calendar's recent and upcoming events, take the watchout.run link off the relevant event — the 📋 anchor recovers the whole plan — and decode it with read_workout_link), else ask the runner to paste any event's link. (c) THE ASK: map the opener to a flow instead of interviewing — a finished run ("back from my run", "that was rough") = the REVIEW flow: fetch the latest Strava activity AND the planned session it matches (by start time) and compare lap by lap (review_session_guide); "what's today / this week?" = deliver or tune the ALREADY-planned session (tune_session_guide), never design a new one over it; only a new goal, a new race, or no plan anywhere = the design workflow below. 0. GATE — expect a data source: the runner's Strava MCP connected in the same client, or training history in your project/memory. Prefer those whenever they exist — a Strava MCP reads Strava directly. ONLY when neither is readable (e.g. ChatGPT, where no Strava MCP exists) fall back to get_strava_activities and hand the runner its activities_url: they fetch their recent runs in the browser and send them back with one tap (the inline card collects them; the paste block and full export remain as fallbacks) — then interview from real data instead of blind. Saved ROUTES come back the same way via pull_strava_routes. Then: before your first make_workout_links call for a runner you MUST have either (a) a runner profile in your own memory (routes with lengths in meters, paces with provenance), or (b) run runner_profile_interview (also the 'setup_runner' prompt) to create one. Do NOT defer this because "it's just an easy run" — easy runs also start at a real door and end somewhere the runner can stop. Skip only if the runner explicitly asks for a generic workout, and say so when you deliver it. 1. PERSONALIZE — call workout_design_guide and follow its method, and get_weather (the runner's CURRENT coords + start time — travelling means the latest activity's start point, not the home base) for outdoor sessions. The .workout file executes by distance/time only; making the session fit the runner's real terrain (their usual loop, its length, the hills, where they can start and stop) is YOUR job at design time. Every distance in the spec must trace back to a real route: a "4000m block" is a smell when the runner's loop is 3200 m. Somewhere the runner (or you) doesn't know — travel, a race trip? Get a route polyline first and ground it with check_route (real distance + elevation); never invent geography — the design guide has the protocol. Pass course_fit to make_workout_links stating the mapping. If the runner has a Strava MCP (or similar) connected, pull their routes, lap lengths, elevation and paces from it instead of asking. 2. Verify the spec with check_session first (lap fit, totals, warnings — server-side math beats mental math), then call make_workout_links. Always pass date (YYMMDD) so the title is searchable on the watch, and pick an emoji by session type: 🔥 intervals/threshold, 🟢 easy run, 🛣️ long run, ⚡ hills/fartlek, 🏁 race. 3. Give the user page_url — one shareable page with "Add to Apple Watch" and "Add to Calendar" buttons, plus an Apple Watch / Garmin toggle. Raw links are also returned: gen_url (Apple .workout file), fit_url (Garmin FIT file — Garmin Connect cannot import it; deliver via intervals.icu sync or USB copy to GARMIN/NewFiles, the page explains both), ics_url (calendar event file: opening it lets the runner pick which of their calendars it goes in), gcal_url (Google Calendar). Know the runner's watch from their profile and speak to it — Garmin runner? Call garmin_delivery_guide for the push-to-watch options (an intervals.icu MCP is the good one). Echo the plan and total distance/time in plain words so the runner can veto before importing. 4. iPhone flow for the .workout file: download -> open in the Fitness app -> "Add to Apple Watch". Garmin flow: see fit_url above. 5. LIFECYCLE — before the run: if the runner mentions weather, time pressure, feeling off or travel, call tune_session_guide and retune without a questionnaire (reuse workout_uuid so the watch replaces the session). Don't have the session in memory? Any WatchOut link the runner pastes decodes with read_workout_link — links carry full session state (this server stores nothing), including the uid you need for the retune. SHOW THE ARITHMETIC on every change: a distance, pace, rep count or session that moves ships WITH what produced it — was → now, the constraint, and the numbers (check_session totals, the week's km). A new number the runner cannot check is a guess with a link attached; this holds hardest when THEY pushed for the change (see tune_session_guide). 6. VERSIONS — results carry generated_with (the server build that made them): save it with the manifest/uuid in your memory. The page/calendar links you hand out are also stamped (v=<version>), so the runner's calendar remembers the vintage even when your memory doesn't: read_workout_link surfaces generated_with and, when the link is older than the server, changes_since — the material entries, pre-digested. Otherwise call whats_new(since=<saved version>) yourself — material entries deserve ONE line to the runner and an offered refresh (same uids, seq+1), nothing more. Entries with interview_note mean the INTERVIEW process changed: run runner_profile_interview(since=<the profile's Profile version>) and ask only the new questions. After the run: call review_session_guide and START with its version check (its server_version vs the profile's Profile version — weeks of post-run-only chats is exactly where re-interviews get missed), then grade the laps against the plan (the feedback goes in your chat reply; the public Strava description stays terse, factual and health-free), update the profile, ask before changing the plan when something's off, and WRITE the log with log_to_strava UNASKED — the log is part of the review, not a favour the runner has to request (the sanctioned write path: the runner's own browser applies it, the token never leaves them) — or two paste-ready text blocks (title + description) when the runner would rather copy by hand. Strava's official MCP connector stays read-only; never suggest token/API-key workarounds. A session that landed OUT OF THE ORDINARY opens the plan question — EITHER direction: a big overshoot at low effort says the paces are stale just as loudly as a bad day says the load is too high. Show show_plan_progress, then ASK; never rewrite the plan silently, and never let a strong session pass as if nothing happened. Prompts tune_today and review_run expose both as one-click actions. PROGRESS — "how is my plan going?", a weekly check-in, or right after a review: answer with show_plan_progress and its inline dashboard, never a text-only summary. The tool is stateless — the card is exactly as good as what you pass, so gather BEFORE calling, not after the runner asks where the plot went: every session from the manifest in slot order with status per past slot (completed/missed — an unreported slot renders as an honest gap), planned_km and planned_pace_s_per_km from the plan, and per completed session the Strava actuals from the review flow — actual_km, actual_min, pace_s_per_km AND avg_hr. Pace + HR on 3+ completed sessions is what unlocks the pace-at-HR fitness plot; missing HR is one list_activities/get_activity_* call away. The result tells you what it could NOT draw: data_gaps names every missing visual, the exact field it needs and where that number comes from, and data_gaps_note says to fill them and CALL AGAIN before showing the runner — a half-empty card presented as the whole picture is the failure mode here. The full set worth gathering once and reusing every week: per completed session pace_s_per_km + avg_hr + actual_min + actual_km, elevation_gain_m and temp_c (terrain/heat normalization — hills and heat move pace more than fitness does), planned_pace_s_per_km per session; from the profile lthr_bpm + hr_rest_bpm (the CTL/ATL/TSB fitness curve, which updates on every easy run); and anchors[] — a race, time trial or the sustained part of a quality session as { date, distance_m, duration_s } — plus race_distance_m, which turn into a real VDOT/Critical-Speed race prediction instead of a trend line extended. Anchors calibrate weekly; Efficiency Factor and CTL track the days between. Give the plot its reference line: race_date + goal_pace_s_per_km AND goal_hr_bpm (the heart rate that goal pace lives at), plus targets[] — the plan's part-goals as dated checkpoints YOU design, each WITH hr_bpm. A pace means nothing without the intensity it was run at: 4:27 at race effort and 6:02 on an easy run are different currencies, and the tool converts between them only through the runner's OWN pace/HR slope, which it can fit once the completed runs span ≥12 bpm. Until then it WITHHOLDS the goal verdict (verdict insufficient_data + goal_note, and the card draws no goal line): tell the runner "the aerobic base is steady; race-pace evidence starts with the first threshold session" — never "you are behind". Corollary for PLAN DESIGN: a block whose first weeks are all easy cannot produce race-pace evidence, so design part-goals in phases — aerobic checkpoints (easy-run HR) during base, threshold/race checkpoints from the first quality session on — and make sure the plan actually contains a session that tests each checkpoint. Store outcomes in your manifest as reviews land, so the next progress call is one tool call, not an excavation. 7. PERSISTENCE — your memory is a cache; the runner's CALENDAR is the durable copy (every event carries its watchout.run link, and the link IS the state). Host has calendar tools (a Google Calendar connector or similar)? PREFER writing the returned calendar_event / calendar.events objects directly over handing the runner an .ics: direct writes update in place (no seq/'(rev N)' dance — that exists only for ICS re-import). Say so on the tool call — calendar_delivery: 'agent' — so the inline card shows '✓ On your calendar' instead of Add-to-Calendar buttons whose import would duplicate the events you wrote. Any later chat recovers a session or a whole plan by listing the runner's events and calling read_workout_link on the watchout.run links found there — the 📋 anchor event carries the plan link, and every plan session's link names its plan_id (pass it back on retunes so membership survives). ONE calendar, chosen once: use the calendar saved in the runner's profile; none saved → list their calendars, look for a training one, and if nothing clearly fits ASK the runner — create a dedicated training calendar or pick an existing one — then save the choice (name + id) in the profile and use it from then on. No calendar tools? The .ics flow works as before, and a pasted link recovers everything the same way. Format knowledge (device-verified on iOS 26): - Warmup/cooldown are a PER-SESSION DECISION, not a ritual. Use one only when it earns its place: as the transport leg between the runner's door and where the session happens (distance form '2000m@5:45', sized to the real leg), or as genuine preparation/flush around quality work (time form '15@5:45'). A door-to-door easy run needs NEITHER — the easy pace is its own warmup; pass 'none' for both. Also takes an open goal ('open', ends on tap). Always set both explicitly: omitting the arg silently applies a legacy default 10-min warmup/cooldown (kept so old links render unchanged) and the tool warns. Match the runner's plan: if their sessions are written in distance, use distance here too. Work steps are always distance goals in meters (or timed with t-steps). - Work steps without a pace ("2000") are valid — use them as alert-free floats. - Timed work steps: t<seconds>[@alert] (t30 = 30 s hard, t300@4:25 = 5 min at pace) — for hill strides, fartlek, and stride-outs where distance is the wrong unit. - Pace alerts are min:sec per km. A range shows on the watch as fast–slow. - HR alerts: h<lo>-<hi> in bpm (e.g. 2000@h140-155, wu=12@h120-140) — the right lever on heavy climbs, heat or cold where pace targets mislead. - Rests are in seconds (r90 = 1:30). - Calendar event times are local to the timezone argument (IANA name, default Europe/Oslo) — set it for runners outside Norway. - A start time is a commitment: take the hour from the runner's profile training windows, or ASK the runner — never invent one. Scheduling at 00:00 is always wrong; midnight is treated as a placeholder and warned. - Same spec -> same UUID: re-importing the same workout never duplicates on the watch. - Planning a WEEK or block, not one session? make_plan_links builds every session in one call: one plan page, ONE .ics import for the whole plan, one bulk Garmin send — and a manifest you MUST save (plan_id + slot uids): it is what lets later retunes and reschedules update instead of duplicate.

Known tools 22

make_workout_links

Create the links for one running workout: page_url (shareable page with watch/FIT and calendar buttons), gen_url (Apple .workout), fit_url (Garmin FIT), plus calendar artifacts when start is given.

Potential side effects
runner_profile_interview

The one-time 'grill me' setup interview method: mine the runner's Strava history for routes/laps/hills/paces, confirm one decision at a time, and save the resulting profile in YOUR memory (this server stores nothing).

Inferred read-only
make_plan_links

Create a whole training plan (any horizon) in one call: plan_url (page with Add-all-to-Calendar and Send-all-to-Garmin), ics_url (ONE import = whole plan; ics_upsert_url for Google), a per-session manifest, `calendar` (ready-to-write events + a 📋 anchor for hosts with calendar tools — prefer writing those directly), and optionally ONE intervals.icu bulk body.

Potential side effects
show_plan_progress

Render the runner's plan progress as an at-a-glance card + computed stats.

Inferred read-only
read_workout_link

Decode a pasted WatchOut link (/w, /ics, /gen, /fit, /plan, /plan.ics) back into make_workout_links-shaped fields + uid + revision — the link IS the state, so this recovers any session or plan in a fresh chat (ask the runner to paste it from the calendar event's URL row).

Inferred read-only
whats_new

Server changelog for version awareness.

Inferred read-only
pull_strava_routes

Read the runner's SAVED Strava routes when your own Strava tools can't (most can't — the official connector is activities-only).

Inferred read-only
fetch_strava_handoff

Collect what the runner sent from a WatchOut page (routes, activities, or an applied-confirmation — see pull_strava_routes / get_strava_activities / log_to_strava).

Inferred read-only
now

The server clock — call it FIRST in a fresh conversation (or whenever unsure what day it is) to anchor 'today': your own sense of date drifts.

Inferred read-only
workout_design_guide

Returns the method for designing course-aware, personal workouts: how to learn the runner's routes/laps/elevation/paces (from their Strava MCP when connected), align reps to lap length so sessions start and finish at known points, handle hills, and sanity-check totals.

Inferred read-only
check_session

Deterministic calculator/validator for a session spec — do the lap math HERE, never in your head.

Inferred read-only
check_route

Deterministic route calculator — ground truth for course-aware design.

Inferred read-only
tune_session_guide

Method for adjusting TODAY's planned session to reality — weather, time budget, body signals, travel — with zero friction: gather silently, apply the right lever, regenerate with the same workout_uuid so the watch replaces the old version.

Inferred read-only
check_plan

Deterministic validator for the SHAPE OF THE WEEKS — check_session does one session's arithmetic, this does the plan's.

Inferred read-only
pace_zones

The runner's training paces, computed from a real performance instead of worked out in your head: easy / marathon / threshold / interval / repetition bands from Daniels' VDOT equations, each with what it is FOR and a `dsl` field ready to paste into a blocks step.

Inferred read-only
adapt_plan_guide

Method for changing the WEEKS, not today's run: sessions missed, illness (the neck rule + return-to-run ramp), injury, travel, a race that moved, and the taper.

Inferred read-only
review_session_guide

Method for the post-run debrief: map Strava laps 1:1 to the planned steps, grade each rep, ask 2-3 sharp questions, update the profile, and log to Strava unasked.

Potential side effects
get_weather

Hourly weather for the runner's route, past AND future: future times return the MET Norway (yr.no) forecast for session planning; past times (e.g.

Inferred read-only
garmin_delivery_guide

Returns the ranked methods for getting a workout onto a Garmin watch: intervals.icu MCP (create the planned workout there; it auto-syncs to Garmin), direct intervals.icu API call with our FIT as base64, or manual download.

Potential side effects
workout_format_help

Returns full documentation of the workout spec format and verified knowledge about the Apple .workout file format.

Inferred read-only
log_to_strava

After a run, prepare a Strava activity update the runner applies with one tap: returns update_url (their browser writes via their OWN Strava app; the token never touches this server; omit activity_id and they pick the run there).

Potential side effects
get_strava_activities

FALLBACK ONLY — for hosts with NO Strava integration at all (chiefly ChatGPT); any real Strava tool (a Strava MCP, list_activities, get_activity_*) always goes first.

Inferred read-only

CONNECT WITH APPROVAL

Client installation

Review this server and its permissions before adding it. Secret placeholders must be set locally.

Codex

~/.codex/config.toml

[mcp_servers.watchout]
url = "https://watchout.run/mcp"
enabled = true
Claude Code

.mcp.json

{
  "mcpServers": {
    "watchout": {
      "type": "http",
      "url": "https://watchout.run/mcp"
    }
  }
}
Claude Desktop

Settings → Connectors → Add custom connector

Name: watchout
Remote MCP URL: https://watchout.run/mcp

Add this remote URL as a custom connector in Claude Desktop. Availability depends on the user plan and workspace policy.

Cursor

.cursor/mcp.json

{
  "mcpServers": {
    "watchout": {
      "url": "https://watchout.run/mcp"
    }
  }
}
Visual Studio Code

.vscode/mcp.json

Add to Visual Studio Code
{
  "servers": {
    "watchout": {
      "type": "http",
      "url": "https://watchout.run/mcp"
    }
  }
}
Generic MCP

Client-specific MCP configuration

{
  "name": "watchout",
  "transport": "streamable-http",
  "url": "https://watchout.run/mcp"
}
MCP Inspector

Run the official MCP Inspector locally and enter the indexed Streamable HTTP endpoint.

TRUST AND VERIFICATION EVIDENCE

Loading Trust v2 evidence…

Checking the associated registrable domain. The BuiltWith key remains server-side.

Indexed

Evidence is source-attributed and does not guarantee that a third-party server is safe. Risk labels are conservative metadata heuristics.