Skip to content

Create and manage training targets

Once polar-flow-mcp is running and your Polar account is linked, you manage workouts through plain-language Claude conversations. This guide covers the intensity vocabulary and the create / list / edit / delete flow, with worked examples.

Claude calling create_training_target

Polar Flow uses five zones (Z1–Z5) on each of three metrics: heart rate, power, and speed/pace. When creating training targets, you specify HR intensity using either a label (preferred) or a numeric zone; power and speed intensity use a numeric zone only.

LabelHR zoneCoaching meaning
easyZ1–2Recovery pace. Conversational, fully aerobic. Shakeout runs, warm-ups, recovery jogs.
aerobicZ2Easy aerobic / base building. Comfortable, sustainable for long runs. Bulk of weekly volume.
tempoZ3Steady-state, comfortably hard. Marathon to half-marathon effort.
thresholdZ4Lactate threshold. Hard, sustainable for ~30–60 min. Classic 1km repeats, cruise intervals.
vo2maxZ5Very hard. Short repeats (1–5 min) at near-maximal aerobic effort.

The label → zone mapping is a fact of the API, not a coaching opinion. Prefer labels in conversation — they are more readable. Use a numeric zone (hr_zone: 4) only when someone explicitly says “zone 4”; precedence when more than one intensity key is given is hr_zone > power_zone > speed_zone > label.

For cycling (or another power-capable sport) and pace-based work, use power_zone (1–5) or speed_zone (1–5) instead. Both are zone indices, not raw watts or km/h/min-per-km values — Polar Flow has no field for a literal physical threshold on a phase. Each zone’s actual range is computed from the athlete’s Sport Profile (FTP, threshold pace), which this server doesn’t read; if someone gives you a wattage or pace target, ask which zone it falls in rather than converting it.

Ask Claude for the session you want:

“Create a 5x1km threshold session with 2-minute recovery for next Thursday”

Claude resolves the date, maps “threshold” to Z4, and calls create_training_target with the phase tree:

{
"name": "5x1km Threshold",
"date": "2026-05-21",
"time": "18:00",
"sport_id": 1,
"phases": [
{ "type": "warmup", "duration_s": 600 },
{
"type": "repeat",
"reps": 5,
"goal": { "distance_m": 1000 },
"intensity": { "label": "threshold" },
"recovery": { "duration_s": 120 }
},
{ "type": "cooldown", "duration_s": 300 }
]
}

The new target shows up in the Polar Flow diary immediately, and Claude reports the target ID so you can reference it later.

Two things worth knowing about the structure:

  • repeat blocks need at least 2 reps. A single continuous zoned effort — “40 minutes at threshold”, “8 km at tempo” — is one block phase (see Zoned steady effort below), not a repeat; an unzoned one with no structure is a simple run (see below).
  • Warm-up and cool-down are not added automatically — they’re a coaching choice. The only defaults the tool itself applies are time = 18:00 and sport_id = 1 (running).

See the create_training_target reference for the full argument schema.

For an easy run with no interval structure, you don’t need phases at all — just a total duration or distance:

“Put an easy 35-minute run on Friday”

{
"name": "Easy 35 min",
"date": "2026-06-14",
"duration_s": 2100,
"sport_id": 1
}

This is a VOLUME target: set exactly one of duration_s, distance_m or kcal (a calorie goal, e.g. "kcal": 500) and omit phases. A VOLUME target has no intensity field at all — if the effort needs a zone, use a phases list instead (see below).

Two more goal-less or single-line shapes:

  • No goal at all — leave out every goal argument and the target is FREE: just the sport on that day (“Run on Sunday, whatever I feel like”).

  • Race pace — give both duration_s and distance_m and the watch paces you to that distance in that time:

    “10 km in 45 minutes on Saturday morning”

    { "name": "10k @ 4:30", "date": "2026-06-20", "time": "08:00",
    "duration_s": 2700, "distance_m": 10000 }

For a single continuous zoned effort and no warm-up/cool-down/interval structure, use one block phase with intensity set and a custom name — it does not need to be wrapped in a repeat. Its goal is duration_s or distance_m:

“40-minute tempo run tomorrow, no warmup”

{
"name": "Tempo run",
"date": "2026-06-15",
"sport_id": 1,
"phases": [
{ "type": "block", "name": "Tempo run", "duration_s": 2400,
"intensity": { "label": "tempo" } }
]
}

Chain block phases to pace a race or long run by km segments, each with its own zone or zone range (zone_max makes the zone a range’s lower bound):

“Half-marathon on the 25th at 9:00: km 1–5 low Z3, km 5–15 Z1 to Z3, then free up to Z4”

{
"name": "Semi-marathon",
"date": "2026-10-25",
"time": "09:00",
"sport_id": 1,
"phases": [
{ "type": "block", "name": "Km 1-5", "distance_m": 5000,
"intensity": { "hr_zone": 3 } },
{ "type": "block", "name": "Km 5-15", "distance_m": 10000,
"intensity": { "hr_zone": 1, "zone_max": 3 } },
{ "type": "block", "name": "Km 15-21", "distance_m": 6100,
"intensity": { "hr_zone": 3, "zone_max": 4 } }
]
}

A repeat can hold its own phases — any number of them, including one more repeat (as in the Flow editor, which allows two levels). manual: true on a phase makes the watch wait for a lap press before moving on, handy for a rest whose length depends on the athlete:

“3 sets of: 400 m in Z4, then 2 × (30 s sprint, 30 s float), then walk until I’m ready”

{
"name": "Ladder sets",
"date": "2026-06-18",
"phases": [
{ "type": "warmup", "duration_s": 900 },
{ "type": "repeat", "reps": 3, "phases": [
{ "type": "block", "name": "400", "distance_m": 400, "intensity": { "hr_zone": 4 } },
{ "type": "repeat", "reps": 2, "phases": [
{ "type": "block", "name": "Sprint", "duration_s": 30, "intensity": { "hr_zone": 5 } },
{ "type": "block", "name": "Float", "duration_s": 30, "intensity": { "label": "easy" } } ] },
{ "type": "block", "name": "Walk", "duration_s": 120, "manual": true } ] },
{ "type": "cooldown", "duration_s": 600 }
]
}

A nested repeat can’t be the last phase of its parent: Polar would flatten it and drop its reps, so the tool refuses it — put a phase after it (here, “Walk”). Limits follow Polar’s editor: 2–99 reps, at most 20 phases as written and 200 once repeats are unrolled.

For a brick or a duathlon, give exercises — one entry per sport, each with sport_id and its own goal — instead of the single-sport arguments. Every sport needs the same kind of goal (all phases, all one volume metric, all a race pace, or none):

{ "name": "Brick", "date": "2026-06-21",
"exercises": [
{ "sport_id": 2, "phases": [ { "type": "block", "distance_m": 30000, "intensity": { "power_zone": 2, "zone_max": 3 } } ] },
{ "sport_id": 1, "phases": [ { "type": "block", "duration_s": 1200, "intensity": { "hr_zone": 3 } } ] }
] }

Polar’s update can’t add or remove a sport, so editing a multi-sport target means sending all of its sports in exercises, in the order get_training_target lists them; to change the set of sports, delete the target and create it again.

“What do I have planned this week?” / “Show me my workouts for the next two weeks”

Claude calls list_training_targets with from_date set to today and to_date set to the end of the requested range (default: today through +30 days), then formats the result as a readable summary with each target’s ID, name, date, and sport.

update_training_target is a full replace — there are no patch semantics. To change one field, Claude first reads the target with get_training_target, modifies the returned body, then writes it back with the same shape plus the target_id. Just ask:

“Move Thursday’s threshold session to Friday at 07:00”

“Delete the threshold session on Thursday” / “Remove the workout with ID 12345”

If you didn’t give an ID, Claude lists matching targets, confirms which one you mean (name + date), then calls delete_training_target. If the target was already removed (e.g. through the Polar app), Claude tells you plainly — that is not an error.

“Create a 5x1km threshold session with 2-minute recovery for next Thursday”

One workout with warmup, five 1km threshold repeats with 2-minute recoveries, and a cooldown, scheduled for that Thursday.

“3x1km at threshold, then 2x500m at vo2max, this Saturday”

{
"name": "Threshold + VO2max ladder",
"date": "2026-05-15",
"phases": [
{ "type": "warmup", "duration_s": 600 },
{
"type": "repeat", "reps": 3,
"goal": { "distance_m": 1000 },
"intensity": { "label": "threshold" },
"recovery": { "duration_s": 90 }
},
{
"type": "repeat", "reps": 2,
"goal": { "distance_m": 500 },
"intensity": { "label": "vo2max" },
"recovery": { "duration_s": 180 }
},
{ "type": "cooldown", "duration_s": 300 }
]
}

“5x4 minutes at power zone 4, 3 minutes easy between, on the bike Sunday”

{
"name": "5x4min Power Z4",
"date": "2026-05-17",
"sport_id": 2,
"phases": [
{ "type": "warmup", "duration_s": 600 },
{
"type": "repeat", "reps": 5,
"goal": { "duration_s": 240 },
"intensity": { "power_zone": 4 },
"recovery": { "duration_s": 180 }
},
{ "type": "cooldown", "duration_s": 300 }
]
}

power_zone is a Polar zone index (1–5), not a wattage — see Intensity vocabulary above. Use speed_zone the same way for pace-based work on foot or bike.

“Build me a 12-week marathon plan starting in three weeks”

For a multi-session plan, Claude asks clarifying questions first (peak mileage, long-run day, rest days), proposes week 1 for your review, then creates sessions one week at a time after confirmation. This keeps you in control and prevents accidental calendar pollution.

  • Raw power/pace thresholds — power_zone and speed_zone only accept a Polar zone index (1–5); there’s no way to target a literal wattage or min/km pace, on this server or in the underlying Polar Flow API itself.
  • Adding or removing a sport on an existing target — Polar’s update keeps the sports it isn’t sent and ignores new ones; delete and recreate.
  • Activity uploads — this server can read completed sessions (summary + details), but it does not create them from device data. Polar’s app/watch is the source of truth. (You can log a manual result — see create_training_session.)
  • Polar account creation or device sync — outside this server’s scope.