Skip to main content
Glama

progression_trend

Tracks estimated one-rep max progression for a single exercise, returning session-by-session data for charting strength gains over time.

Instructions

Top-set e1RM over time for a single exercise. Returns a per-session series suitable for charting.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
exercise_template_idYes
sinceNo
max_pagesNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The `progression_trend` async function is the core handler. It takes an `exercise_template_id`, optional `since`, and `max_pages`, collects sets for that exercise, computes e1RM per session via Epley formula, returns a sorted series of (date, e1rm_kg) plus a linear slope in kg/week.
    async def progression_trend(
        exercise_template_id: str,
        since: str | None = None,
        max_pages: int = MAX_PAGES_DEFAULT,
    ) -> dict[str, Any]:
        """Top-set e1RM over time for a single exercise. Returns a per-session series
        suitable for charting.
        """
        since_dt = _parse_dt(since)
        sets = await _collect_sets_for_exercise(client, exercise_template_id, max_pages)
        # bucket by date, take best e1RM per session
        per_day: dict[str, float] = {}
        for s, when in sets:
            w, r = s.get("weight_kg"), s.get("reps")
            if w is None or r is None or r <= 0 or r > 15 or s.get("type") == "warmup":
                continue
            dt = _parse_dt(when)
            if since_dt and dt and dt < since_dt:
                continue
            if dt is None:
                continue
            day = dt.date().isoformat()
            e1rm = _epley(w, r)
            if e1rm > per_day.get(day, 0):
                per_day[day] = e1rm
        series = [{"date": d, "e1rm_kg": round(v, 1)} for d, v in sorted(per_day.items())]
        if len(series) >= 2:
            slope_per_week = _slope_per_week(series)
        else:
            slope_per_week = None
        return {
            "series": series,
            "slope_kg_per_week": slope_per_week,
            "text": (
                f"{len(series)} sessions; "
                f"slope ~{slope_per_week:+.2f}kg/week" if slope_per_week is not None
                else f"{len(series)} sessions"
            ),
        }
  • Input schema defined via function signature: `exercise_template_id: str` (required), `since: str | None` (optional ISO-8601), `max_pages: int` (default 30).
    async def progression_trend(
        exercise_template_id: str,
        since: str | None = None,
        max_pages: int = MAX_PAGES_DEFAULT,
    ) -> dict[str, Any]:
  • The `_slope_per_week` helper computes a least-squares linear regression slope from the e1RM series, scaled to kg/week.
    def _slope_per_week(series: list[dict[str, Any]]) -> float:
        """Simple least-squares slope of e1RM vs. day-index, scaled to per-week."""
        xs = []
        ys = []
        base = datetime.fromisoformat(series[0]["date"])
        for pt in series:
            d = datetime.fromisoformat(pt["date"])
            xs.append((d - base).days)
            ys.append(pt["e1rm_kg"])
        n = len(xs)
        mx = sum(xs) / n
        my = sum(ys) / n
        num = sum((x - mx) * (y - my) for x, y in zip(xs, ys))
        den = sum((x - mx) ** 2 for x in xs) or 1.0
        slope_per_day = num / den
        return slope_per_day * 7.0
  • The `_collect_sets_for_exercise` helper iterates workouts and collects all sets matching a given exercise_template_id.
    async def _collect_sets_for_exercise(
        client, template_id: str, max_pages: int,
    ) -> list[tuple[dict[str, Any], str | None]]:
        out: list[tuple[dict[str, Any], str | None]] = []
        async for w in _iter_workouts(client, max_pages):
            when = w.get("start_time") or w.get("end_time")
            for ex in w.get("exercises") or []:
                if ex.get("exercise_template_id") != template_id:
                    continue
                for s in ex.get("sets") or []:
                    out.append((s, when))
        return out
  • The `register_all` function in `tools/__init__.py` calls `analytics.register(mcp, ctx)` which registers all analytics tools including `progression_trend`.
    """Tool registration. Each module exposes `register(mcp, ctx)` to attach its tools."""
    
    from . import analytics, folders, routines, templates, webhooks, workouts
    
    
    def register_all(mcp, ctx) -> None:
        workouts.register(mcp, ctx)
        routines.register(mcp, ctx)
        folders.register(mcp, ctx)
        templates.register(mcp, ctx)
        webhooks.register(mcp, ctx)
        analytics.register(mcp, ctx)
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden. It discloses return type ('per-session series') but does not mention any behavioral traits like authentication requirements, rate limits, side effects, or pagination behavior via the max_pages parameter. The description neither contradicts any annotations (none exist) nor provides deep transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very short (one sentence plus a fragment), which makes it concise but at the expense of completeness. It is front-loaded with the core purpose, but the brevity forces the user to infer key details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has 3 parameters, low schema coverage (0%), and no annotations, the description is insufficient. It does not explain 'top-set e1RM', how the series is computed, or the effect of parameters like since and max_pages. An output schema exists but is not referenced, and the description fails to provide a complete picture for safe invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. However, it only mentions 'single exercise' and 'over time', failing to explain the parameters: exercise_template_id (which exercise), since (start date filtering), and max_pages (pagination). The field names are somewhat self-explanatory, but max_pages is not, and the description adds no additional meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns 'top-set e1RM over time for a single exercise' as a per-session series for charting. It uses a specific verb ('Returns') and resource ('top-set e1RM over time'), and the scope ('single exercise') distinguishes it from siblings like estimate_one_rep_max, which estimates a single e1RM.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for viewing progression trends but provides no explicit guidance on when to use this tool versus alternatives (e.g., estimate_one_rep_max for single estimates, or get_workout for raw data). No exclusions or alternative recommendations are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Vellarasan/hevy-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server