list_workouts
Retrieve a paginated list of your workout summaries in reverse-chronological order. Use this to review recent training sessions before requesting detailed set-by-set data.
Instructions
List the user's workouts in reverse-chronological order.
Use this first when the user asks about "recent workouts", "last N sessions",
"what did I train on Monday", etc. Each item is a summary; call
get_workout(workout_id) for full set-by-set detail when the user asks
about specific weights, RPE, or progression.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-indexed page number. | |
| page_size | No | Workouts per page. Hevy caps this at 10. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/hevy_mcp/tools/workouts.py:25-44 (handler)The list_workouts tool handler function. It fetches workouts from the Hevy API with pagination, formats them into readable text, and returns the result. Registered via @mcp.tool() decorator inside the register() function.
async def list_workouts( page: int = Field(1, ge=1, description="1-indexed page number."), page_size: int = Field(10, ge=1, le=WORKOUT_PAGE_SIZE_MAX, description="Workouts per page. Hevy caps this at 10."), ) -> dict[str, Any]: """List the user's workouts in reverse-chronological order. Use this first when the user asks about "recent workouts", "last N sessions", "what did I train on Monday", etc. Each item is a *summary*; call `get_workout(workout_id)` for full set-by-set detail when the user asks about specific weights, RPE, or progression. """ data = await client.get("/workouts", params={"page": page, "pageSize": page_size}) items = _items(data) return { "text": "\n\n".join(format_workout(w) for w in items) or "(no workouts on this page)", "data": data, "page": page, "page_count": data.get("page_count") if isinstance(data, dict) else None, } - src/hevy_mcp/tools/__init__.py:6-7 (registration)Registration entry point: register_all() calls workouts.register(mcp, ctx) which registers list_workouts (and other workout tools) onto the MCP server.
def register_all(mcp, ctx) -> None: workouts.register(mcp, ctx) - src/hevy_mcp/formatters.py:45-58 (helper)The format_workout helper used by list_workouts to convert workout JSON into human-readable text (title, exercises, sets).
def format_workout(w: dict[str, Any]) -> str: title = w.get("title") or "(untitled)" start = w.get("start_time") or "" lines = [f"# {title} {start}".rstrip()] if w.get("description"): lines.append(w["description"]) for ex in w.get("exercises") or []: ex_title = ex.get("title") or ex.get("exercise_template_id") or "exercise" sets = ex.get("sets") or [] set_strs = [_fmt_set(s) for s in sets] lines.append(f"- {ex_title}: " + ", ".join(set_strs) if set_strs else f"- {ex_title}") if ex.get("notes"): lines.append(f" note: {ex['notes']}") return "\n".join(lines) - The _items helper extracts the list of workout items from the API response, trying multiple possible keys (workouts, items, results, data).
def _items(data: Any) -> list[dict[str, Any]]: if isinstance(data, dict): for k in ("workouts", "items", "results", "data"): v = data.get(k) if isinstance(v, list): return v if isinstance(data, list): return data return [] def _unwrap(data: Any, key: str) -> dict[str, Any]: if isinstance(data, dict) and key in data and isinstance(data[key], dict): return data[key] return data if isinstance(data, dict) else {} - src/hevy_mcp/schemas.py:41-48 (schema)The Workout Pydantic schema class used for input validation in create_workout and update_workout (not directly used by list_workouts, but part of the workout tool family).
class Workout(_Base): id: str | None = None title: str description: str | None = None start_time: datetime | None = None end_time: datetime | None = None is_private: bool = False exercises: list[WorkoutExercise] = Field(default_factory=list)