PeakSets

PeakSets Connector

Documentation for the PeakSets Model Context Protocol (MCP) server: how to connect, how authorization works, and every tool an AI client can call.

Overview

PeakSets is a workout planning and logging app for strength training and HYROX-style race simulations. The connector lets an AI assistant read a user’s PeakSets training data and, when the user asks, write plans back to their account. Plans the AI creates appear in the PeakSets app immediately. Results the user logs at the gym are available to the AI right after the workout.

The server exposes 22 tools (12 read-only, 7 write, 3 destructive) and 5 prompts. Every call is scoped to the authenticated user’s own data.

Connect

Server URL
https://mcp.peaksets.com
Transport
Streamable HTTP (/mcp is also accepted)
Authorization
OAuth 2.0 authorization code flow with PKCE (S256)
Account
A PeakSets account (Apple, Google, or email sign-in)

Add https://mcp.peaksets.com as a custom connector in your AI client. The client discovers the authorization server, registers itself, and opens the PeakSets sign-in page. After you sign in, PeakSets tools are available in your conversations.

Supported clients include Meta Muse (launching first) and Claude, with ChatGPT and Gemini to follow. Any MCP client that supports remote servers with OAuth should work.

Authorization

PeakSets implements the MCP authorization spec. Clients never see the user’s password or social sign-in credentials.

Protected resource metadata
/.well-known/oauth-protected-resource
Authorization server metadata
/.well-known/oauth-authorization-server
Dynamic client registration
POST /oauth/register (RFC 7591)
Authorization endpoint
/oauth/authorize
Token endpoint
POST /oauth/token
Revocation endpoint
POST /oauth/token (RFC 7009)
Grant types
authorization_code, refresh_token
PKCE
S256 only. Requests without PKCE or using plain are rejected.
Client auth
Public clients (none), client_secret_basic, client_secret_post
Access tokens
Bearer tokens in the Authorization header, valid for 1 hour

Lifecycle.

  1. The client calls the MCP endpoint without a token and receives 401 with a WWW-Authenticate header pointing to the protected resource metadata.
  2. The client reads the metadata and registers itself at /oauth/register.
  3. The user is sent to /oauth/authorize, signs in with Apple, Google, or email and password, and approves access.
  4. PeakSets redirects back with an authorization code. The client exchanges it, with its PKCE verifier, for an access token and a refresh token.
  5. The client calls tools with Authorization: Bearer <token> and refreshes with the refresh_token grant when the access token expires.
  6. Users can disconnect at any time from their AI client. Clients can revoke tokens at the revocation endpoint.

Tools

Each tool carries MCP annotations. Read tools set readOnlyHint: true. Delete tools set destructiveHint: true and only delete what the user asks for. Completed workouts can’t be deleted through the connector.

ToolAreaAccess
get_profileProfileRead
update_profileProfileWrite
list_plansWorkout plansRead
get_planWorkout plansRead
get_plan_by_idWorkout plansRead
create_planWorkout plansWrite
update_planWorkout plansWrite
delete_planWorkout plansDestructive
create_race_simulationRace simulationsWrite
update_race_simulationRace simulationsWrite
get_resultsResults & historyRead
get_historyResults & historyRead
history_exercise_detailResults & historyRead
get_personal_recordsResults & historyRead
get_exercise_libraryResults & historyRead
create_programProgramsWrite
list_programsProgramsRead
get_programProgramsRead
delete_programProgramsDestructive
save_noteCoaching notesWrite
get_notesCoaching notesRead
delete_noteCoaching notesDestructive

Profile

Read the user’s goals, equipment, weekly session target, weight unit, training preferences, and liked/disliked exercises.

No parameters.

Full description and JSON Schema
Read the user's profile including goals, equipment, session frequency, training preferences, exercise likes/dislikes, and onboarding state. When onboarding.mcpCompleted is false, run the MCP welcome flow (or invoke the peaksets/welcome prompt) before other work. Pay close attention to preferences.trainingPreferences — they contain specific instructions about exercise selection, equipment preferences, physical limitations, and training style that must be respected when creating plans.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {}
}

Update goals, equipment, session frequency, weight unit, and free-text training preferences.

ParameterTypeDescription
goalsstring[]Training goals, e.g. ["build_muscle", "lose_fat"]
equipmentstring[]Available equipment, e.g. ["full_gym"] or ["dumbbells", "bench"]
sessionsPerWeekintegerTarget sessions per week
weightUnit"lbs" | "kg"Preferred weight unit
preferencesstring[]Free-text training preference notes to ADD to the existing list.
removePreferencesstring[]Exact preference strings to REMOVE from the existing list.
markMcpOnboardingCompletebooleanSet to true at the end of the MCP welcome flow to record that the user has been onboarded to PeakSets through their AI assistant.
Full description and JSON Schema
Update the user's profile: goals, available equipment, session frequency, weight unit, and free-text training preferences. Also used at the end of the MCP welcome flow by passing markMcpOnboardingComplete: true. Training preferences accumulate — each call adds to the existing list unless removePreferences is also passed.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "goals": {
      "description": "Training goals, e.g. [\"build_muscle\", \"lose_fat\"]",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "equipment": {
      "description": "Available equipment, e.g. [\"full_gym\"] or [\"dumbbells\", \"bench\"]",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "sessionsPerWeek": {
      "description": "Target sessions per week",
      "type": "integer",
      "minimum": 1,
      "maximum": 7
    },
    "weightUnit": {
      "description": "Preferred weight unit",
      "type": "string",
      "enum": [
        "lbs",
        "kg"
      ]
    },
    "preferences": {
      "description": "Free-text training preference notes to ADD to the existing list.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "removePreferences": {
      "description": "Exact preference strings to REMOVE from the existing list.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "markMcpOnboardingComplete": {
      "description": "Set to true at the end of the MCP welcome flow to record that the user has been onboarded to PeakSets through their AI assistant.",
      "type": "boolean"
    }
  }
}

Workout plans

list_plans

Read

List workout plans by date range and/or status, ordered by scheduled date.

ParameterTypeDescription
fromstringInclusive lower bound on session.date (YYYY-MM-DD)
tostringInclusive upper bound on session.date (YYYY-MM-DD)
status"planned" | "active" | "completed"Filter by workout status
Full description and JSON Schema
List workout plans matching an optional date range and/or status. Use this instead of get_plan when building a calendar, schedule, or upcoming-sessions view — get_plan is ordered by creation time and can hide an earlier-date plan behind a later-created one. Results are ordered by session.date ascending. Drill into any item with get_plan_by_id.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "from": {
      "description": "Inclusive lower bound on session.date (YYYY-MM-DD)",
      "type": "string"
    },
    "to": {
      "description": "Inclusive upper bound on session.date (YYYY-MM-DD)",
      "type": "string"
    },
    "status": {
      "description": "Filter by workout status",
      "type": "string",
      "enum": [
        "planned",
        "active",
        "completed"
      ]
    }
  }
}

get_plan

Read

Read the most recent workout plan, optionally filtered by status.

ParameterTypeDescription
status"planned" | "active" | "completed" | "latest"Filter by plan status. "latest" returns the most recent regardless of status. Default: "latest"
Full description and JSON Schema
Read a single workout plan. Returns the most recent plan matching the status filter, ordered by creation time. For a calendar of upcoming plans or to drill into a specific plan by date, prefer list_plans + get_plan_by_id.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "status": {
      "default": "latest",
      "description": "Filter by plan status. \"latest\" returns the most recent regardless of status.",
      "type": "string",
      "enum": [
        "planned",
        "active",
        "completed",
        "latest"
      ]
    }
  }
}

Fetch one workout plan by ID.

ParameterTypeDescription
workoutId requiredstringThe workout document ID
Full description and JSON Schema
Fetch a specific workout plan by workoutId. Use this to drill into a plan returned by list_plans or referenced by get_personal_records/get_program.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "workoutId": {
      "type": "string",
      "description": "The workout document ID"
    }
  },
  "required": [
    "workoutId"
  ]
}

create_plan

Write

Create a strength workout plan. It appears in the PeakSets app immediately.

ParameterTypeDescription
session requiredobject
exercises requiredobject[]Exercises in the session
programIdstringLink this workout to a program created via create_program
Full description and JSON Schema
Write a new workout plan to the user's PeakSets account. The plan appears in their app immediately. Multiple planned workouts can be queued — each new plan is added to the queue. Use canonical format '{Equipment} {Movement}' for exercise names (e.g. "Barbell Bench Press"). Before creating a plan, call get_profile to check preferences, equipment, and exercise likes/dislikes, and respect all stated preferences. Call get_history to reuse canonical exercise names and check recent patterns.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "session": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "description": "Session name, e.g. \"Upper Body A\""
        },
        "date": {
          "type": "string",
          "description": "Target date in YYYY-MM-DD format"
        },
        "week": {
          "description": "Program week number",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "programPhase": {
          "description": "e.g. hypertrophy, strength, deload",
          "type": "string"
        },
        "notes": {
          "description": "Session-level notes for the user",
          "type": "string"
        }
      },
      "required": [
        "name",
        "date"
      ]
    },
    "exercises": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Exercise name, e.g. \"Barbell Bench Press\""
          },
          "notes": {
            "description": "Exercise notes — either a plain string or structured { coaching?, form?, videoUrl? }.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "object",
                "properties": {
                  "coaching": {
                    "description": "Session-specific coaching advice (progression, intensity adjustments).",
                    "type": "string"
                  },
                  "form": {
                    "description": "Technique cues, especially for new exercises or when the user reported form issues.",
                    "type": "string"
                  },
                  "videoUrl": {
                    "description": "YouTube demonstration video URL from reputable coaches. Only include for exercises the user is unfamiliar with.",
                    "type": "string"
                  }
                }
              }
            ]
          },
          "order": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991,
            "description": "Exercise order in the session (1-based)"
          },
          "type": {
            "default": "weight_reps",
            "description": "Exercise type. weight_reps (default): barbell/dumbbell/cable/machine lifts (including dumbbell lunges, barbell lunges). bodyweight_reps: pull-ups, dips, push-ups, bodyweight lunges. weight_distance: weighted carries. weight_duration: weighted holds. duration: planks, wall sits. distance: running, rowing. bodyweight_distance: unweighted walking movements (rare — most lunges should use weight_reps or bodyweight_reps). cardio: treadmill, bike.",
            "type": "string",
            "enum": [
              "weight_reps",
              "weight_distance",
              "weight_duration",
              "bodyweight_reps",
              "bodyweight_distance",
              "duration",
              "distance",
              "cardio"
            ]
          },
          "sets": {
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "targetWeight": {
                  "type": "number",
                  "description": "Target weight in user's preferred unit"
                },
                "targetReps": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991,
                  "description": "Target rep count"
                },
                "restSeconds": {
                  "default": 90,
                  "description": "Rest time after this set in seconds",
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                "targetDistance": {
                  "description": "Target distance in meters (for distance/carry exercises)",
                  "type": "number"
                },
                "targetDuration": {
                  "description": "Target duration in seconds (for timed exercises like planks)",
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                }
              },
              "required": [
                "targetWeight",
                "targetReps"
              ]
            },
            "description": "Sets for this exercise"
          },
          "supersetGroup": {
            "description": "Superset group number — exercises with the same group number alternate sets",
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "name",
          "order",
          "sets"
        ]
      },
      "description": "Exercises in the session"
    },
    "programId": {
      "description": "Link this workout to a program created via create_program",
      "type": "string"
    }
  },
  "required": [
    "session",
    "exercises"
  ]
}

update_plan

Write

Edit a planned strength workout in place: exercises, sets, weights, or session details.

ParameterTypeDescription
workoutId requiredstringThe workout ID to update
sessionobjectUpdated session details. Only provided fields are changed.
exercisesobject[]Full replacement exercise list.
Full description and JSON Schema
Update an existing planned workout in place. Use this to modify exercises, sets, weights, or session details instead of creating a new plan. The workout must still be in 'planned' status. Pass the workoutId from a previous create_plan response or from get_plan/list_plans.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "workoutId": {
      "type": "string",
      "description": "The workout ID to update"
    },
    "session": {
      "description": "Updated session details. Only provided fields are changed.",
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "description": "Session name, e.g. \"Upper Body A\""
        },
        "date": {
          "type": "string",
          "description": "Target date in YYYY-MM-DD format"
        },
        "week": {
          "description": "Program week number",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "programPhase": {
          "description": "e.g. hypertrophy, strength, deload",
          "type": "string"
        },
        "notes": {
          "description": "Session-level notes for the user",
          "type": "string"
        }
      },
      "required": [
        "name",
        "date"
      ]
    },
    "exercises": {
      "description": "Full replacement exercise list.",
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Exercise name, e.g. \"Barbell Bench Press\""
          },
          "notes": {
            "description": "Exercise notes — either a plain string or structured { coaching?, form?, videoUrl? }.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "object",
                "properties": {
                  "coaching": {
                    "description": "Session-specific coaching advice (progression, intensity adjustments).",
                    "type": "string"
                  },
                  "form": {
                    "description": "Technique cues, especially for new exercises or when the user reported form issues.",
                    "type": "string"
                  },
                  "videoUrl": {
                    "description": "YouTube demonstration video URL from reputable coaches. Only include for exercises the user is unfamiliar with.",
                    "type": "string"
                  }
                }
              }
            ]
          },
          "order": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991,
            "description": "Exercise order in the session (1-based)"
          },
          "type": {
            "default": "weight_reps",
            "description": "Exercise type. weight_reps (default): barbell/dumbbell/cable/machine lifts (including dumbbell lunges, barbell lunges). bodyweight_reps: pull-ups, dips, push-ups, bodyweight lunges. weight_distance: weighted carries. weight_duration: weighted holds. duration: planks, wall sits. distance: running, rowing. bodyweight_distance: unweighted walking movements (rare — most lunges should use weight_reps or bodyweight_reps). cardio: treadmill, bike.",
            "type": "string",
            "enum": [
              "weight_reps",
              "weight_distance",
              "weight_duration",
              "bodyweight_reps",
              "bodyweight_distance",
              "duration",
              "distance",
              "cardio"
            ]
          },
          "sets": {
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "targetWeight": {
                  "type": "number",
                  "description": "Target weight in user's preferred unit"
                },
                "targetReps": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991,
                  "description": "Target rep count"
                },
                "restSeconds": {
                  "default": 90,
                  "description": "Rest time after this set in seconds",
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                "targetDistance": {
                  "description": "Target distance in meters (for distance/carry exercises)",
                  "type": "number"
                },
                "targetDuration": {
                  "description": "Target duration in seconds (for timed exercises like planks)",
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                }
              },
              "required": [
                "targetWeight",
                "targetReps"
              ]
            },
            "description": "Sets for this exercise"
          },
          "supersetGroup": {
            "description": "Superset group number — exercises with the same group number alternate sets",
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "name",
          "order",
          "sets"
        ]
      }
    }
  },
  "required": [
    "workoutId"
  ]
}

delete_plan

Destructive

Delete a planned or in-progress workout. Completed workouts can’t be deleted.

ParameterTypeDescription
workoutId requiredstringThe workout ID (from create_plan response, list_plans, etc.)
Full description and JSON Schema
Delete a planned or active workout by workoutId. Use this to undo a create_plan mistake or cancel a pending session. Completed workouts (status: 'completed') are not deletable via this tool — they represent logged training history and should be removed from the PeakSets app directly if really needed. Idempotent: returns deleted: false with a reason if the workout is missing or completed.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "workoutId": {
      "type": "string",
      "description": "The workout ID (from create_plan response, list_plans, etc.)"
    }
  },
  "required": [
    "workoutId"
  ]
}

Race simulations

Create a timed race-style workout: HYROX-style or DEKA-style sims, AMRAPs, EMOMs, or “for time” WODs.

ParameterTypeDescription
raceType required"hyrox" | "deka" | "crossfit_wod" | "amrap" | "emom" | "for_time" | "custom"hyrox | deka | crossfit_wod | amrap | emom | for_time | custom
raceName requiredstringDisplay name, e.g. "Hyrox 60% Simulation", "Murph", "Cindy"
intensitynumberScaling factor 0.1–1.0 relative to a full race. 1.0 = full intensity. Default: 1
timeCapintegerTime cap in seconds. Required for amrap and emom; optional for everything else.
scheduledDatestringYYYY-MM-DD. Defaults to today if omitted.
stations requiredobject[]Ordered list of stations. Order is the array index.
notesstringOverall race coaching notes shown to the user before they start.
Full description and JSON Schema
Create a race-style workout in PeakSets.

Use this for race-style workouts where the user trains against a clock with no rest between stations — Hyrox, DEKA, CrossFit Hero WODs, AMRAPs, EMOMs, "for time" workouts. NOT for strength training (use create_plan for that).

Use intensity (0.1–1.0) to scale a full race down — generally scale distances linearly and weights in 5lb/2.5kg increments. Use your judgment about whether to scale distance, reps, or weight based on what is practical for the user.

Patterns:
- HYROX: alternate run stations with work stations. Standard layout is 8 runs interleaved with SkiErg, Sled Push, Sled Pull, Burpee Broad Jump, Row, Farmers Carry, Sandbag Lunges, Wall Balls (in that order). For a 60% sim, use 600m runs and scale work stations.
- AMRAP (e.g. Cindy: 5 pull_ups + 10 push_ups + 15 squats AMRAP 20:00): emit one round of stations with labels "Round 1" / "Round 2" / ... and set timeCap to the AMRAP duration in seconds. The app cycles rounds until timeCap expires.
- EMOM: emit stations with target.duration = 60 each. Set timeCap = stations.length * 60.
- Murph: emit one ordered list — Run 1mi, 100 pull_ups, 200 push_ups, 300 squats, Run 1mi.
- "for time" CrossFit WODs: emit movements as ordered stations with rep/weight targets; no timeCap.

To edit an existing race plan (intensity, stations, date, etc.) use update_race_simulation — do not delete and recreate.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "raceType": {
      "type": "string",
      "enum": [
        "hyrox",
        "deka",
        "crossfit_wod",
        "amrap",
        "emom",
        "for_time",
        "custom"
      ],
      "description": "hyrox | deka | crossfit_wod | amrap | emom | for_time | custom"
    },
    "raceName": {
      "type": "string",
      "description": "Display name, e.g. \"Hyrox 60% Simulation\", \"Murph\", \"Cindy\""
    },
    "intensity": {
      "default": 1,
      "description": "Scaling factor 0.1–1.0 relative to a full race. 1.0 = full intensity.",
      "type": "number",
      "minimum": 0.1,
      "maximum": 1
    },
    "timeCap": {
      "description": "Time cap in seconds. Required for amrap and emom; optional for everything else.",
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "scheduledDate": {
      "description": "YYYY-MM-DD. Defaults to today if omitted.",
      "type": "string"
    },
    "stations": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "run",
              "ski_erg",
              "sled_push",
              "sled_pull",
              "burpee_broad_jump",
              "row",
              "farmers_carry",
              "sandbag_lunges",
              "wall_balls",
              "pull_ups",
              "push_ups",
              "squats",
              "box_jumps",
              "kb_swings",
              "thruster",
              "deadlift",
              "clean",
              "jerk",
              "custom"
            ],
            "description": "Station movement type"
          },
          "label": {
            "type": "string",
            "description": "Display label, e.g. \"Run 1\", \"Sled Push\", \"Round 1\" (for AMRAPs use \"Round N\")"
          },
          "target": {
            "type": "object",
            "properties": {
              "distance": {
                "description": "Target distance in meters (for run, row, ski_erg, sled, etc.)",
                "type": "number"
              },
              "reps": {
                "description": "Target reps (for wall_balls, burpees, push_ups, etc.)",
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "duration": {
                "description": "Target duration in seconds (for fixed-time stations like a 60s plank)",
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "weight": {
                "description": "Weight in user's preferred unit (sled push, farmers carry, sandbag, thruster, etc.)",
                "type": "number"
              }
            },
            "description": "What the user is racing against. At least one of distance/reps/duration should be set."
          },
          "notes": {
            "description": "Optional pacing tips or coaching cues",
            "type": "string"
          }
        },
        "required": [
          "type",
          "label",
          "target"
        ]
      },
      "description": "Ordered list of stations. Order is the array index."
    },
    "notes": {
      "description": "Overall race coaching notes shown to the user before they start.",
      "type": "string"
    }
  },
  "required": [
    "raceType",
    "raceName",
    "stations"
  ]
}

Edit a planned race simulation: intensity, stations, date, or notes.

ParameterTypeDescription
workoutId requiredstringThe race workout ID to update
raceType"hyrox" | "deka" | "crossfit_wod" | "amrap" | "emom" | "for_time" | "custom"Change the race type (e.g. hyrox → custom)
raceNamestringChange the display name
intensitynumberChange the scaling factor (0.1–1.0)
timeCapinteger | nullChange the time cap in seconds. Pass null to remove an existing cap.
scheduledDatestringChange the scheduled date (YYYY-MM-DD)
stationsobject[]Full replacement station list. Order is the array index.
notesstring | nullChange the session-level coaching notes. Pass null to clear them.
Full description and JSON Schema
Update an existing race-style workout in place.

Use this when the user wants to tweak a race plan that has already been created — change intensity, swap a station, push the date out, etc. The workout must still be in planned status (you cannot edit a race that is in progress or completed). Pass the workoutId from a previous create_race_simulation response or from get_plan / list_plans.

Only the fields you provide are changed. stations, when provided, is a full replacement (the order in the array becomes the new station order). To swap a single station, send the entire updated stations[] back. To change just intensity or scheduled date, send only that field.

For non-race plans (strength workouts), use update_plan instead.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "workoutId": {
      "type": "string",
      "description": "The race workout ID to update"
    },
    "raceType": {
      "description": "Change the race type (e.g. hyrox → custom)",
      "type": "string",
      "enum": [
        "hyrox",
        "deka",
        "crossfit_wod",
        "amrap",
        "emom",
        "for_time",
        "custom"
      ]
    },
    "raceName": {
      "description": "Change the display name",
      "type": "string"
    },
    "intensity": {
      "description": "Change the scaling factor (0.1–1.0)",
      "type": "number",
      "minimum": 0.1,
      "maximum": 1
    },
    "timeCap": {
      "description": "Change the time cap in seconds. Pass `null` to remove an existing cap.",
      "anyOf": [
        {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        {
          "type": "null"
        }
      ]
    },
    "scheduledDate": {
      "description": "Change the scheduled date (YYYY-MM-DD)",
      "type": "string"
    },
    "stations": {
      "description": "Full replacement station list. Order is the array index.",
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "run",
              "ski_erg",
              "sled_push",
              "sled_pull",
              "burpee_broad_jump",
              "row",
              "farmers_carry",
              "sandbag_lunges",
              "wall_balls",
              "pull_ups",
              "push_ups",
              "squats",
              "box_jumps",
              "kb_swings",
              "thruster",
              "deadlift",
              "clean",
              "jerk",
              "custom"
            ]
          },
          "label": {
            "type": "string"
          },
          "target": {
            "type": "object",
            "properties": {
              "distance": {
                "type": "number"
              },
              "reps": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "duration": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "weight": {
                "type": "number"
              }
            }
          },
          "notes": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "label",
          "target"
        ]
      }
    },
    "notes": {
      "description": "Change the session-level coaching notes. Pass `null` to clear them.",
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "workoutId"
  ]
}

Results & history

Read completed workouts with planned vs. actual for every set (strength) or per-station splits and RPE (races), plus the user’s feedback notes.

ParameterTypeDescription
limitintegerNumber of recent workouts to return (mixed strength + race) Default: 5
exercisestringFilter strength items to exercises whose name includes this substring (case-insensitive). Race items are unaffected by this filter.
Full description and JSON Schema
Read completed workouts with planned vs actual comparison for every set (strength) or per-station splits and RPE (race). Each item carries a kind discriminator: "strength" returns the per-set planned/actual/result triples; "race" returns stations[] with actualDuration, RPE, and overallDifficulty. Race stations include both target (planned) and actual (what the user actually did — distance/reps/weight). When actual differs from target, the user scaled the station; analyze why and adjust future programming. Pay close attention to workoutNotes and per-exercise feedbackNotes (or per-station notes for races) — they contain direct user feedback.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "limit": {
      "default": 5,
      "description": "Number of recent workouts to return (mixed strength + race)",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "exercise": {
      "description": "Filter strength items to exercises whose name includes this substring (case-insensitive). Race items are unaffected by this filter.",
      "type": "string"
    }
  }
}

Aggregated trends: weekly volume, per-muscle volume, estimated 1RM progression, plateaus, deload signals, and streaks.

ParameterTypeDescription
weeksintegerNumber of weeks of history to analyze Default: 8
exercisestringFilter to a specific exercise for detailed progression
Full description and JSON Schema
Read aggregated workout history with interactive charts: weekly volume, per-muscle-group volume, exercise progression, estimated 1RM trends, plateau detection, deload recommendations, and streak stats. Use this to make informed programming decisions. Per-station detail (target vs actual, RPE) lives in get_results; recentRaces here is a name/finish-time rollup for trend-spotting.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "weeks": {
      "default": 8,
      "description": "Number of weeks of history to analyze",
      "type": "integer",
      "minimum": 1,
      "maximum": 52
    },
    "exercise": {
      "description": "Filter to a specific exercise for detailed progression",
      "type": "string"
    }
  }
}

Set-by-set history for one exercise.

ParameterTypeDescription
exerciseName requiredstringExact exercise name to look up
weeksintegerNumber of weeks of history to return Default: 12
Full description and JSON Schema
Returns set-by-set history for a specific exercise across workouts.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "exerciseName": {
      "type": "string",
      "description": "Exact exercise name to look up"
    },
    "weeks": {
      "default": 12,
      "description": "Number of weeks of history to return",
      "type": "integer",
      "minimum": 1,
      "maximum": 52
    }
  },
  "required": [
    "exerciseName"
  ]
}

All-time bests per exercise: heaviest set, best volume, and estimated 1RM.

ParameterTypeDescription
limitintegerMaximum number of records to return Default: 50
exercisestringFilter to exercises whose name includes this substring (case-insensitive)
Full description and JSON Schema
All-time bests per exercise: best weight × reps, best total volume, and estimated 1RM — each with the date and workoutId where it was set. Use this to build PR dashboards or answer "what's my best bench?" questions without re-deriving from get_history. Records are only counted once an exercise has been performed in at least 2 separate workouts (qualifiedWorkouts >= 2) — matches the app's PR-detection rule.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "limit": {
      "default": 50,
      "description": "Maximum number of records to return",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "exercise": {
      "description": "Filter to exercises whose name includes this substring (case-insensitive)",
      "type": "string"
    }
  }
}

Browse the global exercise catalog with muscle groups, equipment, form cues, and the user’s liked/disliked flags.

ParameterTypeDescription
muscleGroupstringFilter to a single UI muscle group: Chest | Back | Shoulders | Arms | Legs | Core
equipmentstringFilter by equipment category, e.g. 'barbell', 'dumbbell', 'cable'
searchstringSubstring match on exercise name (case-insensitive)
Full description and JSON Schema
Return the full global exercise catalog (or a filtered slice) with muscle groups, equipment category, default exercise type, form cues, and the user's per-exercise preference (liked/disliked) where set. Use this when suggesting new exercises, offering substitutions, or building a filterable picker.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "muscleGroup": {
      "description": "Filter to a single UI muscle group: Chest | Back | Shoulders | Arms | Legs | Core",
      "type": "string"
    },
    "equipment": {
      "description": "Filter by equipment category, e.g. 'barbell', 'dumbbell', 'cable'",
      "type": "string"
    },
    "search": {
      "description": "Substring match on exercise name (case-insensitive)",
      "type": "string"
    }
  }
}

Programs

Create a multi-week training program that plans can belong to.

ParameterTypeDescription
name requiredstringProgram name
weeksintegerNumber of weeks in the program
phasestringe.g. hypertrophy, strength, peaking, deload
notesstringAdditional notes about the program
Full description and JSON Schema
Create a training program (e.g. "8-Week Hypertrophy Block"). Individual workout sessions can reference this program via programId when calling create_plan.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Program name"
    },
    "weeks": {
      "description": "Number of weeks in the program",
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "phase": {
      "description": "e.g. hypertrophy, strength, peaking, deload",
      "type": "string"
    },
    "notes": {
      "description": "Additional notes about the program",
      "type": "string"
    }
  },
  "required": [
    "name"
  ]
}

List the user’s training programs.

No parameters.

Full description and JSON Schema
List training programs the user has created, ordered by most recently created first. Use get_program to drill into any item for week-by-week planned vs completed.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {}
}

Read a program with its week-by-week planned vs. completed breakdown.

ParameterTypeDescription
programId requiredstringThe program document ID (from list_programs or create_program)
Full description and JSON Schema
Fetch a training program plus its week-by-week planned vs completed breakdown. Joins all workouts with matching programId and groups them by session.week. weeksData is ordered by week number; workouts with no week number end up in the null bucket at the end.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "programId": {
      "type": "string",
      "description": "The program document ID (from `list_programs` or `create_program`)"
    }
  },
  "required": [
    "programId"
  ]
}

delete_program

Destructive

Delete a program. Workouts that referenced it are kept.

ParameterTypeDescription
programId requiredstringThe program ID (from list_programs or the create_program response)
Full description and JSON Schema
Delete a program by programId. Any workouts that referenced the program keep their programId field as-is — they become "orphaned" but otherwise unaffected, and will still appear in list_plans / get_plan_by_id. The response reports how many workouts now reference the deleted program so you can decide whether to update or recreate it. Returns deleted: false if the program was already gone.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "programId": {
      "type": "string",
      "description": "The program ID (from list_programs or the create_program response)"
    }
  },
  "required": [
    "programId"
  ]
}

Coaching notes

save_note

Write

Save a coaching analysis note, or a short “Coach’s Take” attached to a workout.

ParameterTypeDescription
content requiredstringThe analysis note content
type required"weekly_review" | "program_note" | "general" | "coach_take"Note type. weekly_review: summary of a week of training. program_note: note about program adjustments. general: any other coaching observation. coach_take: ONE short, specific, quotable coaching insight (max 240 chars) tied to a single completed workout via workoutId — the user can share it as a card. Example: "Sub-90 fit on the ergs but sub-100 pace on lower body — add sled volume this block and 1:28 is realistic." One take per workout; saving again replaces it.
periodStartstringStart of the analysis period (YYYY-MM-DD)
periodEndstringEnd of the analysis period (YYYY-MM-DD)
workoutIdstringRequired for coach_take: the completed workout this take is about
Full description and JSON Schema
Save a coaching analysis note to the user's PeakSets account. Use this after analyzing workout results to preserve insights for future conversations. Weekly reviews, program adjustments, and progress observations should all be saved so they can be retrieved via get_notes later. Call get_notes first to avoid duplicating recent analysis. After analyzing a race or workout, you may also save one short, quotable insight as kind 'coach_take' (requires workoutId) — the user can share it as a card.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "content": {
      "type": "string",
      "description": "The analysis note content"
    },
    "type": {
      "type": "string",
      "enum": [
        "weekly_review",
        "program_note",
        "general",
        "coach_take"
      ],
      "description": "Note type. weekly_review: summary of a week of training. program_note: note about program adjustments. general: any other coaching observation. coach_take: ONE short, specific, quotable coaching insight (max 240 chars) tied to a single completed workout via workoutId — the user can share it as a card. Example: \"Sub-90 fit on the ergs but sub-100 pace on lower body — add sled volume this block and 1:28 is realistic.\" One take per workout; saving again replaces it."
    },
    "periodStart": {
      "description": "Start of the analysis period (YYYY-MM-DD)",
      "type": "string"
    },
    "periodEnd": {
      "description": "End of the analysis period (YYYY-MM-DD)",
      "type": "string"
    },
    "workoutId": {
      "description": "Required for coach_take: the completed workout this take is about",
      "type": "string"
    }
  },
  "required": [
    "content",
    "type"
  ]
}

get_notes

Read

Read previously saved coaching notes.

ParameterTypeDescription
type"weekly_review" | "program_note" | "general" | "coach_take"Filter by note type (coach_take: quotable per-workout takes saved via save_note)
limitintegerNumber of notes to return Default: 10
Full description and JSON Schema
Retrieve coaching analysis notes you've previously saved with save_note. Use this to review past assessments before creating new plans or analyzing current results — it gives you continuity across conversations.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "type": {
      "description": "Filter by note type (coach_take: quotable per-workout takes saved via save_note)",
      "type": "string",
      "enum": [
        "weekly_review",
        "program_note",
        "general",
        "coach_take"
      ]
    },
    "limit": {
      "default": 10,
      "description": "Number of notes to return",
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    }
  }
}

delete_note

Destructive

Delete a coaching note.

ParameterTypeDescription
noteId requiredstringThe note ID (from get_notes or the save_note response)
Full description and JSON Schema
Delete a coaching note by noteId. Use this to undo a note you just saved or to remove one that's no longer relevant. Returns deleted: true on success, deleted: false if the note was already gone.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "noteId": {
      "type": "string",
      "description": "The note ID (from get_notes or the save_note response)"
    }
  },
  "required": [
    "noteId"
  ]
}

Prompts

The server also offers ready-made prompts that clients can show as shortcuts.

PromptWhat it does
peaksets/welcomeFirst-connect onboarding flow. Introduces PeakSets, nudges the user to complete their profile if needed, and ends with a concrete next step. Invokes get_profile / update_profile at runtime to branch on whether onboarding is already complete.
peaksets/morning-checkinQuick dashboard for the day: today's planned workout, current streak, and a recent PR. Good for opening the app each morning.
peaksets/plan-todayLook at profile, recent history, and last few results, then propose and (on approval) create today's session via create_plan.
peaksets/progress-checkSummarize the last 12 weeks: trends, plateaus, deload signals, and per-muscle-group volume. Offers to open as a live dashboard.
peaksets/pr-dashboardAll-time personal records across every exercise the user has trained. Renders as a live artifact.

Errors

Tool errors are returned as MCP tool results with isError: true and a plain-language message the AI can relay or act on. Auth errors are standard OAuth HTTP responses.

SituationResponse
Missing or expired access token401 with WWW-Authenticate: Bearer … error="invalid_token". The client should refresh or re-authorize.
Authorization request without S256 PKCE400 “Invalid authorization request: The plain PKCE method is not allowed. Use S256 instead.”
Invalid tool argumentsA validation error naming the field that failed.
Unknown workout ID“Error: No workout found with ID "…".”
Editing a workout that already started or finished“Error: Cannot update a workout that is already "completed". Only planned workouts can be edited.”
Using update_plan on a race“Error: This workout is a race. Use update_race_simulation to edit it.”
Deleting something that’s already gone, or a completed workoutNot an error: deleted: false with a reason. Deletes are idempotent.

Data & privacy

See the Privacy Policy and Terms of Service.

Support

Questions, bug reports, or review access: [email protected].