Watch a Moodle course space over time: what is new, what changed, and what you already handled. An MCP server built on moodle-dl.
moodle-watch demonstrates solid tool design with clear, domain-specific naming and comprehensive descriptions. All 12 tools have non-empty descriptions (avg ~180 chars, within baseline 194). Input schemas are present and typed for all tools. However, output schemas are entirely undocumented, responses are JSON strings with no declared structure, forcing LLMs to parse unstructured text. Parameter descriptions are generally good but lack explicit constraints (enums, ranges, patterns). Error handling is minimal; no recovery guidance or actionable error messages visible. Tool composition is clean (single responsibility), but missing pagination metadata (total_count, next_cursor) on list-returning tools.
Everything not handled yet, oldest first. The work queue. An item leaves this list when `mark_handled` records a `read`, `ingested` or `ignored` action for it. Marking it `todo` keeps it here on purpose.
Sections and modules of one course, each item annotated with its state. Use this to navigate a course rather than to search it. For a whole-space lookup by name, use `search`.
List watched courses, with unhandled counts and the next deadline. Each course carries the `rule` that decided its state, so "why does this course not show up" always has an answer. `state` is one of watch, ignore, or pending; a pending course has never been arbitrated by anyone.
Upcoming deadlines, soonest first. `moved` is true when the due date changed since it was first seen, and `moved_from` gives the previous one. No Moodle API reports this; it is known only because the ledger saw the earlier value.
Every revision of one item, and everything that was done with it. `item_key` comes from any other tool's output. Revisions follow the file across changes and moves, so this is the honest answer to "has this been updated since I read it".
Output schemas completely undocumented. All tools return JSON strings with no declared structure. LLMs must parse unstructured text, risking extraction errors and wasting tokens.
No pagination metadata returned by list tools (courses, whats_new, backlog, search). Missing total_count, next_cursor, or has_more fields. Large result sets risk context window exhaustion.
Parameter constraints not formalized. 'since' accepts multiple formats (last_run, all, 7d, ISO date, epoch) but no enum or pattern constraint. 'action' in mark_handled lists valid values in description only, not as enum.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 58 | 2026-07-28+ | v2 |
Record that these items were dealt with. Writes the ledger, not Moodle. `action` is one of read, ingested, ignored, todo. `by` is free text naming who did it, for example `nightly` or `alice`. `note` is the right place for a link to whatever was produced. Handling is per revision: if the file changes later, it returns to the backlog on its own.
Extract the text of one file, by its `file_id`. Reads the local copy when the mirror has it, and downloads through the Moodle token otherwise. `pages` selects PDF pages, for example `1-5` or `3`. Media files return their metadata and no text: a video is not summarised here.
Full-text search over course, section, module and file names. Accent-insensitive: `evaluation` finds `Évaluation`. Supports FTS5 syntax, so `dimension NEAR langagiere` and `program*` work.
Decide whether a course is watched. Use this to answer a pending course. `state` is watch, ignore or pending. A decision made with a `by` other than `policy` is never overwritten by a policy rule afterwards, so the user's answer sticks.
Start a watch cycle in the background and return at once. A full cycle takes minutes, so it never runs inside a tool call. Poll `sync_status` for the result. With `download` true it also fetches files, which takes considerably longer.
Poll the status of a background sync started by `sync`. Returns immediately with the current state. `run_id` comes from the `sync` response. When `finished` is not null, the run is complete.
What appeared, changed or vanished since `since`. The main tool. `since` accepts `last_run` (the default), `all`, a duration like `7d` or `48h`, an ISO date like `2026-09-01`, or an epoch integer. Already handled items are hidden unless `include_handled` is true: the question asked is what remains, not what happened. The reply also carries deadline changes (a `moved_from` means the due date was shifted) and `pending_courses`, which need the user's arbitration.
Error handling absent from visible code. No recovery guidance, error classification, or actionable messages. TokenExpired is handled specially but other failure modes lack guidance.
Destructive operations (mark_handled, set_course_policy, sync) lack confirmation or dry-run support. No idempotent hints or safety guards visible.