Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ notes. This file records important changes to *this package*.

## Unreleased

* `get_workout_for_date` returns the day's `description` as `day_description`.
A routine's per-day notes are where rep ranges, machine substitutions and
form cues live, and the tool that answers "what am I doing today" was
returning the planned numbers without the terms they were written under — a
caller reporting the plan quoted a bare rep count where the routine had
specified a range. Unset descriptions come back as `null`, matching
`day_name`.

## 0.2.0

* `add_exercise_with_sets` returns the created ids, as its docstring always
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ The training-plan tree is the largest group and splits in two: `routines_read` (
| `set_slot_entry_config(slot_entry_id, kind, value, iteration?, operation?, step?, repeat?, weight_unit?, requirements?)` | Add a per-iteration config record. `weight_unit` applies to `kind='weight'`/`'max_weight'` and is recorded on the slot entry. `requirements` gates the step on what was logged — any of `repetitions`, `weight`, `rir`, `rest` |
| `update_slot_entry_config(kind, config_id, value?, iteration?, ..., requirements?)` / `delete_slot_entry_config(kind, config_id)` | Patch / delete a config record (use to bump weight on progression). `requirements=[]` clears an existing gate |
| `add_exercise_with_sets(day_id, exercise_id, sets, reps, weight?, slot_order?, weight_unit?, rir?, entry_type?)` | Convenience: slot + entry + sets/reps configs in one call. Omit `weight` to prescribe sets without a load |
| `get_workout_for_date(routine_id, workout_date?)` | What the routine prescribes on a date (default today): one entry per planned SET, with exercise name, `slot_entry_id`, reps, weight and RiR. Feed its ids into `log_set` |
| `get_workout_for_date(routine_id, workout_date?)` | What the routine prescribes on a date (default today): one entry per planned SET, with exercise name, `slot_entry_id`, reps, weight and RiR, plus the day's own `day_description` notes. Feed its ids into `log_set` |

### Workout logs

Expand Down
12 changes: 12 additions & 0 deletions src/wger_mcp/tools/routines.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,12 @@ async def get_workout_for_date(
repetitions, weight and RiR. Feed routine_id, slot_entry_id and
iteration straight into log_set so the logged set attaches to the plan.

day_description carries the routine's own notes for that day — rep
ranges, machine substitutions, form cues — as the trainee wrote them.
The planned numbers say what to do; the description says on what terms,
and a caller that reports the plan without it quotes a bare rep count
where the routine specified a range.

This is the one call that answers "what am I doing today" and "what is
in this program". Walking days, slots, entries and their configs costs
dozens of requests and returns far more than anyone needs.
Expand Down Expand Up @@ -406,6 +412,12 @@ async def get_workout_for_date(
# A day need not be named, and Unset would not survive the
# tool boundary as JSON.
"day_name": None if isinstance(day.name, Unset) else day.name,
# Where a routine keeps its per-day coaching notes: rep ranges,
# machine substitutions, form cues. Without it a caller has the
# numbers but not the terms they were written under.
"day_description": (
None if isinstance(day.description, Unset) else day.description
),
"is_rest_day": (day.is_rest is True) or not planned,
"planned": planned,
}
Expand Down
14 changes: 13 additions & 1 deletion tests/test_routine_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,13 @@ def _sequence(
"iteration": 3,
"date": day_date.isoformat(),
"label": "Week 3",
"day": {"id": 11, "routine": 7, "name": "Push", "is_rest": is_rest},
"day": {
"id": 11,
"routine": 7,
"name": "Push",
"is_rest": is_rest,
"description": "Working reps = lower end of range: bench 6-8.",
},
"slots": slots,
}
)
Expand Down Expand Up @@ -172,6 +178,9 @@ async def test_returns_slot_entry_ids_for_today(monkeypatch: pytest.MonkeyPatch)

assert out["iteration"] == 3
assert out["day_name"] == "Push"
# The day's notes carry the terms the numbers were written under - a rep
# range here - so a caller reporting the plan can quote them.
assert out["day_description"] == "Working reps = lower end of range: bench 6-8."
assert out["is_rest_day"] is False
assert len(out["planned"]) == 1
entry = out["planned"][0]
Expand Down Expand Up @@ -222,11 +231,14 @@ async def test_unnamed_day_still_answers(monkeypatch: pytest.MonkeyPatch) -> Non
mcp = _register(routines)
sequence = _sequence()
sequence[0].day.name = UNSET
sequence[0].day.description = UNSET
monkeypatch.setattr(routines.routine_date_sequence_gym_list, "asyncio", _Capture(sequence))
_mock_names(monkeypatch)
out = _result(await mcp.call_tool("get_workout_for_date", {"routine_id": "7"}))

assert out["day_name"] is None
# Same treatment as the name: Unset must not survive the tool boundary.
assert out["day_description"] is None
assert len(out["planned"]) == 1


Expand Down
Loading