moodle-watch
Provides a stateful, read-only memory layer over a Moodle instance via a moodle-dl mirror: tools for seeing what is new/changed/vanished since a timestamp or date (whats_new), listing watched and pending courses with the policy rule that decided them, tracking deadlines and due-date changes, working through a backlog of unhandled items, inspecting a course tree annotated with item state, accent-insensitive full-text search over course/section/module/file names, reading document text, viewing the revision history of items and who handled them, marking items handled per revision, setting course policies, and running background sync cycles (detect-only or with downloads).
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@moodle-watchwhat's new in my Moodle since Tuesday?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
moodle-watch
Give your AI agents a memory of your Moodle.
Moodle can tell an agent what exists. It cannot tell it what is new to you, what changed since you last looked, or what you have already dealt with. Every Moodle MCP server out there is a stateless wrapper around the same API: ask it twice, get the same answer twice, learn nothing.
moodle-watch keeps a ledger. That one difference changes what an agent can do.
Why this matters if you work with agents
A stateless tool makes your agent a search box. You ask, it fetches, it forgets. You are still the one who remembers what you already read, notices that a lecturer silently re-uploaded a PDF, and keeps the mental list of what is left. That bookkeeping is the actual work, and no amount of prompting removes it.
With a ledger, the agent holds all three:
It can open with news instead of waiting for a question. whats_new is a
delta, not a dump. "Since Tuesday: four new files in two courses, one deadline
moved a week earlier, and a course you have never triaged just appeared."
It stops re-reading what it already read. Every item carries its handling
state. A nightly agent can ask for the backlog, work through it, call
mark_handled with a link to whatever it produced, and pick up exactly where it
left off tomorrow. No "have I seen this already" heuristics, no re-summarising
the same lecture four times, no burned context.
It notices the thing you would have missed. A lecturer replaces a slide deck two days before the exam. moodle-dl overwrites the file. Moodle sends nothing. Here, the item gets a new revision, and because handling is recorded per revision, it returns to your backlog by itself. You never diff anything by hand again.
A swarm can divide the work. mark_handled takes a by field. Several
agents, or an agent and a human, can share one queue without stepping on each
other, and history shows who did what to which revision.
It never decides behind your back. A newly discovered course lands in
pending: not watched, not ignored, surfaced on every call until somebody
arbitrates. Every course carries the rule that decided it, so "why is this not
showing up" always has an answer. Silent whitelists are how you find out in
March that a module was never being tracked.
And it is read-only against Moodle by construction, not by instruction. The web-service allowlist lives in code; submitting, grading and posting are unreachable. You can hand this to an autonomous agent without wondering what it might click.
Related MCP server: Claude Synergy
How it works
It does not reimplement moodle-dl. It imports it.
moodle-dl what exists, what changed mods, LTI extractors, yt-dlp
moodle-watch since when, what you did the clock, the ledger, MCPEvery course module type, every LTI video extractor, yt-dlp, years of awkward real-world cases: all of it comes along. moodle-watch adds the two things moodle-dl does not have.
Because moodle-dl records what it has, not when it got it. Its
time_stamp column is written only on modification paths; a fresh insert leaves
the default. On a real mirror of 2210 rows, and again on a fresh one of 95,
every single value is 0. And once its notified flag is raised, the seen /
not-seen distinction is gone for good.
So the ledger records three things:
| our own clock, on every revision |
| who did what with an item, per revision |
| why a course is watched or not, and which rule decided |
Handling is keyed on moodle-dl's file_id, and moodle-dl inserts a new row
whenever a file changes. The "it comes back to your backlog" behaviour is not a
feature anyone maintains; it is a property of the schema.
Nothing is ever deleted. A disappearance is recorded as an observation.
Install
pipx install "moodle-watch[documents]"You need a moodle-dl mirror directory. If you do not have one:
moodle-dl --init --sso --path ~/Moodle # or --init -u USER -pw PASSThen point moodle-watch at it and run a first cycle:
export MOODLE_WATCH_MIRROR=~/Moodle
moodle-watch run
moodle-watch coursesThe first run discovers every course you are enrolled in and puts them all in
pending. Decide with a policy file, or one tool call per course.
Tools
Tool | Answers |
| what appeared, changed or vanished since |
| what is watched, what is pending, and the rule that decided |
| what is due, and whether a due date moved |
| everything not handled yet, oldest first |
| sections and modules, each annotated with its state |
| accent-insensitive full text over course, section, module, file names |
| the text of one file, from the mirror or through the token |
| every revision of an item, and everything done with it |
| record that items were dealt with |
| answer a pending course |
| start a cycle in the background, poll it |
Watching and mirroring are the same code with a different argument:
moodle-watch run detects only, moodle-watch run --fetch also downloads,
forums and videos included.
The policy
~/.config/moodle-watch/policy.toml, written for you on first run. Rules are
evaluated in order, first match wins, like a firewall.
default = "ask" # ask | watch | ignore
[[rule]]
match = { startdate_after = "2026-08-01" } # this year only
action = "watch"
[[rule]]
match = { shortname = "*2025*" }
action = "ignore"
[exclude]
extensions = ["mp4"]
larger_than = "500MB"
filenames = ["department-logo.png"] # moodle-dl cannot filter by nameCriteria: course, shortname, fullname, startdate_after,
startdate_before. The [exclude] block is written into moodle-dl's own
config.json, so there is one source of truth. A decision you make by hand is
never overwritten by a rule afterwards.
Running it
Over stdio, for a local client:
{ "mcpServers": { "moodle-watch": {
"command": "moodle-watch-server",
"env": { "MOODLE_WATCH_MIRROR": "/home/you/Moodle" } } } }Over HTTP, behind your own reverse proxy:
MOODLE_WATCH_HOSTS=moodle.example.org moodle-watch-server --httpIt binds loopback only. MOODLE_WATCH_HOSTS feeds the SDK's DNS-rebinding
guard, which validates the Host header; the public name must be listed there
or the proxy gets a 421. See docs/deployment.md and the
templates in deploy/.
Environment
Variable | Meaning |
| the moodle-dl directory. Required. |
| ledger path. Default: |
| policy file. Default: |
| HTTP port. Default: |
| comma-separated public host names |
| only for |
No secret lives in this repository. The Moodle token stays in the mirror's
config.json, where moodle-dl put it.
Diagnosing
moodle-watch doctor # layers 1 to 3
moodle-watch doctor --remote # and the public doorFour layers, deepest to most exposed. Layer 3 is not a health check: it opens a
real MCP session and asserts the answers are coherent with each other. A server
returning 200 while reporting empty courses is not a server that works. The
output never prints the secret URL segment.
When the token expires
Moodle web-service tokens expire, and an SSO-issued one is renewed by hand:
moodle-dl -nt -sso --path ~/Moodlemoodle-watch names this failure mode rather than dying quietly: the tools return
a token_expired payload carrying that exact command, doctor gives it its own
line, and a failed run records it in the ledger.
Honest status
This works. It is also vibe-coded. It was built in one sitting, with an AI,
by someone who is a teacher and not a software maintainer. It is verified rather
than merely hoped for: every claim above was checked against a live Moodle 4.5
instance, there are 40 tests, and the load-bearing behaviours (idempotence,
revisions returning to the backlog, nothing ever deleted, the Host guard) each
have one. Two real design bugs were caught by those tests and fixed. But there
is no roadmap, no support, and no promise of a second sitting.
One thing in particular deserves a warning: moodle-watch drives moodle-dl
through internal classes of a command-line application, which carry no
stability guarantee. That is why the version is pinned, why every import is
confined to engine.py, and why doctor asserts each call site still exists.
It is a deliberate and contained bet, not an oversight, but it is a bet.
So: shared gladly, take it, fork it, do whatever you like with it. If you think the idea is good, the right move is to maintain your own copy, not to wait on this one. Issues may go unanswered. A fork that outgrows this repo would be a happy outcome, not a slight.
Licence
GPL-3.0-or-later, inherited from moodle-dl, which is imported rather than reimplemented.
Available Tools
12 toolsbacklogB
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| course | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of behavioral disclosure, and it does well: it explains list membership, ordering, and the lifecycle rules around `mark_handled` actions. It doesn't mention potential side effects, but this is clearly a read-only list tool and no mutation is implied.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, front-loaded, and every sentence adds meaningful context. The first sentence establishes the core purpose, and the second clarifies the lifecycle behavior without any wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The core backlog concept is clear, but the description omits any explanation of the two input parameters, which are both undocumented in the schema as well. An agent cannot be fully confident about how filtering by course or limiting the list behaves, so the description is not complete enough for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 for the undocumented `limit` and `course` parameters. It does not mention either parameter, nor how they affect the results. The schema only provides names and defaults, leaving the agent to guess that `course` filters and `limit` caps the output.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the resource as the work queue/backlog and states that it contains unhandled items ordered oldest first. It does not use an explicit verb like 'list' or 'retrieve,' and it doesn't directly contrast itself with sibling tools, but the intent is unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use this tool: to see unhandled work, since items leave when `mark_handled` records a `read`, `ingested`, or `ignored` action. However, it never explicitly says when to use it versus alternatives like `history`, `whats_new`, or `deadlines`, leaving that to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
coursesA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| include_ignored | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses the state values and the meaning of 'pending', which is helpful, but it does not explicitly state whether the operation is read-only, nor does it describe any side effects, pagination, or error behavior. The 'List' verb implies read-only, but it is not stated explicitly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded with the main action, followed by valuable context about state and rule. Every sentence adds meaning, with no filler. The structure is clear and easy to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity and the presence of an output schema, the description covers the core purpose and state semantics. However, it omits any explanation of the 'include_ignored' parameter, which is a gap. It also doesn't mention any ordering or pagination, but that may be covered by the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, 'include_ignored', is a boolean with a default of false, and its name is self-explanatory. However, the description does not mention it at all, and since the schema has no description coverage, the agent must infer its meaning from the name alone. This leaves ambiguity about how it interacts with the 'watched courses' filter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'list' and resource 'watched courses', with additional details about unhandled counts and next deadline. This distinguishes it from siblings like 'deadlines' and 'backlog' by specifying the scope as watched courses. The mention of 'rule' and 'state' adds depth to what the tool provides.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not explicitly state when to use this tool versus alternatives, but it implies usage for understanding course states via the 'rule' and 'state' fields. It does not name any sibling tools or provide exclusions, so the guidance is only implied. This is acceptable but not explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
course_treeA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| course | Yes | ||
| only_unhandled | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It communicates that each returned item has a state and implies read-only navigation, but it does not explicitly state that the operation is non-mutating, how state is represented, or how errors/edge cases behave. Adequate for a simple navigation tool, but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no filler. It front-loads the core purpose and then immediately provides routing to the alternative. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter tool with an output schema present, the description covers the core navigation purpose and sibling distinction. However, it leaves a meaningful gap around `only_unhandled`, which an agent would need to understand to invoke the tool correctly in all cases.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description does not explain either parameter. 'Course' is vaguely implied by 'one course', but the meaning of `only_unhandled` and the expected format of `course` are completely absent. The description adds almost no value beyond the raw schema names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly identifies the resource: 'Sections and modules of one course', and clarifies it is a navigation tool rather than a search tool. It distinguishes itself from the sibling `search` by contrasting course-tree navigation with whole-space lookup.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: 'Use this to navigate a course rather than to search it' and names the specific alternative for the opposite case: 'For a whole-space lookup by name, use `search`.' This fully routes the agent to the correct tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
deadlinesC
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.
| Name | Required | Description | Default |
|---|---|---|---|
| within | No | 30d | |
| include_past | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the burden of behavioral disclosure. It adds context about the 'moved' field and its origin (ledger observation), which is useful, but it does not state whether the operation is read-only, has side effects, or any other behavioral traits. Partial transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, with the main purpose front-loaded in the first sentence. It is concise without waste, and the additional note about 'moved' is clearly separated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
While an output schema exists (so return values need not be explained), the description omits any explanation of the input parameters, which are non-trivial with defaults. This leaves a significant gap for correct invocation, making the description incomplete for a tool with these context signals.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description does not mention either parameter ('within' or 'include_past'). An agent receives no explanation of what these parameters mean or how to use them, so the description fails to compensate for the schema gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the tool lists upcoming deadlines sorted soonest first, which is a clear verb and resource. It does not explicitly differentiate from siblings like backlog or whats_new, but the purpose is specific enough that an agent can infer its use.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided on when to use this tool versus alternatives. The description does not mention scenarios, exclusions, or prerequisites, leaving the agent to guess whether this is the right choice for a given request.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
historyA
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".
| Name | Required | Description | Default |
|---|---|---|---|
| item_key | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It reveals a non-obvious behavior: revisions follow the item across changes and moves, so the history stays accurate over time. This is genuinely useful context beyond what the schema or tool name communicates.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no filler. The core purpose is front-loaded, and the second sentence earns its place by explaining how to obtain the parameter and why the tool is valuable for a common question.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema, the description provides enough to invoke it correctly: what it returns, where the parameter comes from, and its key tracking behavior. It could be more explicit about ordering, pagination, or read-only guarantees, but those are not critical given the output schema and low complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only a name and type for item_key with 0% description coverage. The description adds useful provenance—'item_key comes from any other tool's output'—which tells an agent where to source the value. However, it does not clarify the format, constraints, or what qualifies as a valid item_key, so it only partially compensates for the schema gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the resource as the revision history of a single item ('Every revision of one item'), which is distinct from sibling tools like read_document or search. It lacks an explicit action verb like 'retrieves' or 'lists', and its phrasing is more definitional than operational, so it stops short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete guidance: item_key should be obtained from any other tool's output, and this is positioned as the reliable way to answer 'has this been updated since I read it'. It does not explicitly name alternative tools or list exclusion criteria, but the use case and prerequisite are clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_handledA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| by | Yes | ||
| note | No | ||
| action | Yes | ||
| file_ids | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It clearly discloses a write operation ('Writes the ledger'), states it does not affect Moodle, and explains the non-obvious per-revision behavior: if the file changes later, it returns to the backlog. It also gives guidance on `note`. It stops short of idempotency or error behavior, but the key side effects are explicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The entire description is three sentences, each earning its place: purpose/scope, parameter semantics, and the per-revision behavioral caveat. No filler, front-loaded purpose, and markdown formatting improves scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (4 params, 0% schema descriptions, no annotations), the description covers the essential contract: what it does, the write target, parameter formats, and a surprising behavioral rule. It doesn't address idempotency or prerequisites, but the presence of an output schema reduces the need to document return values, leaving few critical gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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. It defines `action` as one of read, ingested, ignored, todo; explains `by` as free text with examples; and positions `note` as the place for a link. `file_ids` is only implied via 'these items,' but the first sentence ties it to the target items, which is sufficient given the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with a specific verb and resource: 'Record that these items were dealt with.' It further narrows scope with 'Writes the ledger, not Moodle,' which distinguishes it from any Moodle-facing sibling tools. Enumeration of the `action` values adds concrete definition.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage—use this when items have been dealt with—and the per-revision caveat gives context, but it never names an alternative tool or explicit when-not-to-use condition. The 'not Moodle' line hints at an exclusion without offering a replacement tool, leaving some routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_documentA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| pages | No | ||
| file_id | Yes | ||
| max_chars | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral burden and does it well: it discloses the local-mirror vs Moodle-download fallback, PDF page selection, and the media-file metadata-only behavior. This gives agents meaningful expectations beyond 'read a file,' though edge cases like truncation or auth are not covered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and well-structured: purpose first, then source behavior, page selection, and media limitation. Every sentence adds useful information without redundancy or fluff. It is easy to scan and fully digest.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema exists and no annotations are provided, the description supplies the key behavioral context needed for correct invocation: single-file scope, page range selection, and media file handling. Minor omissions like `max_chars` semantics are acceptable because the parameter name and default carry that meaning.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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. It clearly explains `file_id` and `pages` with examples ('1-5' or '3'), but it does not mention `max_chars` at all. The name is self-explanatory, but the description partially fills the gap rather than completely covering all parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Extract the text of one file, by its `file_id`.' This clearly states the tool's scope and differentiates it from siblings like search or courses. The focus on a single file by ID leaves no ambiguity about the operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use the tool: when you need the text of a specific file. It also provides useful usage context for `pages` and warns that media files return no text. However, it does not explicitly mention alternatives or state when not to use this tool, so the guidance relies on inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| course | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses useful behavioral traits: accent-insensitivity and FTS5 syntax support. But it does not explicitly state that the tool is read-only, mention pagination, rate limits, or the fact that it only searches names (which is actually mentioned). The behavioral transparency is adequate but has gaps around side effects and operational constraints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences: the first gives the core purpose, the second and third provide concise, valuable syntax details. There is no redundant or filler text, and the most important information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has a moderate parameter set and an output schema, which covers return values. The description explains search scope and special query syntax, which is the main contextual need. It does not explicitly describe how the 'course' parameter filters results, but this is reasonably inferable from the schema. Overall, the description is nearly complete for successful use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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. It adds meaning to the 'query' parameter by specifying what it searches (names) and giving syntax examples (NEAR, wildcard). However, it does not explain the 'limit' or 'course' parameters beyond their schema titles and types, leaving some semantics to inference. The compensation is partial, so a mid-range score is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('search') and explicitly lists the resources ('course, section, module and file names'). It also sets the tool apart from all siblings, none of which are search-related, by clarifying exactly what content is searched.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides clear context for when to use the tool: whenever full-text search over course/section/module/file names is needed. It also gives examples of the FTS5 syntax, making its usage concrete. However, it does not explicitly state when not to use it or name alternatives, so it lacks explicit exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_course_policyA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| by | Yes | ||
| state | Yes | ||
| reason | No | ||
| course_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry behavioral disclosure. It does reveal a meaningful rule: decisions made with a 'by' other than 'policy' are never overwritten by a policy rule. However, it does not discuss side effects, response behavior, or whether an existing non-policy decision can be changed directly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with purpose, then concrete parameter semantics and an important behavioral caveat. Every sentence earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter mutation tool with no annotations and no enums, the description covers the usage trigger, state values, and the key overwrite rule. The optional reason parameter is not explained, but the required parameters are substantially addressed and an output schema exists, making it mostly complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds real meaning by enumerating state values (watch, ignore, pending) and explaining the effect of 'by' versus 'policy'. Since schema description coverage is 0%, this helps, but course_id and especially the optional reason parameter receive no semantic explanation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a concrete action ('Decide whether a course is watched') and a target resource, and adds 'Use this to answer a pending course' to clarify intent. It does not explicitly contrast with sibling tools like mark_handled, so it stops short of full differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides a clear trigger: use this tool when there is a pending course to answer. It does not name alternatives or exclusions, but the usage context is explicit enough for an agent to select the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
syncA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| download | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does well by disclosing asynchronous execution, immediate return, background duration, and the added file-fetching behavior when download is true. It does not mention potential side effects or permission requirements, but the core asynchronous trait is clearly communicated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly written: the first sentence front-loads the core behavior, and the second adds necessary temporal and parameter context. Every sentence earns its place with no redundant or filler wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-optional-parameter async tool, the description covers what the tool does, when it returns, how long work takes, how to retrieve results, and the parameter's effect. The presence of an output schema means return-value details need not be repeated, and the guidance to poll sync_status closes the loop for the agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must add meaning beyond the bare boolean type and title. It explicitly explains that download=true triggers file fetching and longer runtime, implying the default is metadata-only sync. This compensates for the schema gap, though it could have also stated what happens when download is false.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb-resource pair ('Start a watch cycle') and clearly states that the tool returns immediately after launching background work. It also distinguishes itself from the sibling sync_status by framing sync as the initiation step and sync_status as the polling step.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells the agent when to expect completion ('takes minutes', 'never runs inside a tool call') and directs it to poll sync_status for the result. It also explains the conditional usage of the download parameter and its time impact, leaving no ambiguity about invocation flow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sync_statusA
The state of the most recent sync, running or finished.
finished null means it is still going. A non-null error starting with
token_expired means the Moodle token must be renewed by hand; report the
command rather than retrying.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden and delivers meaningfully: it documents that 'finished: null' means the sync is still running, and that a 'token_expired'-prefixed error requires manual token renewal instead of a retry. This goes beyond what an agent could infer from the schema, though it could also mention auth expectations or side-effect-free guarantees.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense paragraphs with no filler: the first sentence front-loads the purpose, and the second adds only the interpretive semantics that an agent actually needs. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter status query with an output schema present, the description covers the essential return-value interpretation and the critical error case. The only real gap is that it doesn't situate the check within the sync workflow, e.g., by saying to consult this before deciding to retry or trigger a sync.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the description correctly adds nothing here; the baseline of 4 for a parameterless tool applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States clearly that the tool reports the state of the most recent sync ('running or finished'), which adds specificity beyond the bare name. However, it is a noun phrase rather than a verb+resource and does not explicitly differentiate itself from the sibling 'sync' tool, though the read-vs-trigger distinction is fairly inferable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides actionable guidance on interpreting results and handling the 'token_expired' error ('report the command rather than retrying'). But it never says when to call this tool versus alternatives such as 'sync' or 'history' — the selection context is left implicit, and the guidance is about reacting to output rather than choosing the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
whats_newA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| since | No | last_run | |
| course | No | ||
| include_handled | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and delivers: it reveals that handled items are hidden unless `include_handled` is true, that the reply carries deadline shifts (`moved_from`) and `pending_courses` needing user arbitration, and it documents the accepted forms of `since`. It does not explain `course`/`limit` behavior or explicitly state this is a read-only operation, but those are secondary gaps here.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short paragraphs, each earning its place: a front-loaded purpose sentence, the critical `since` grammar, and the handled-items/reply semantics. No filler or repetition; dense but readable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shape is covered elsewhere, and the description usefully adds semantics for `moved_from` and `pending_courses`. But with 0% schema coverage and no annotations, the unexplained `course` and `limit` parameters are a notable gap for a tool positioned as 'the main tool'.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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. It richly documents `since` (full grammar: `last_run`, `all`, durations like `7d`/`48h`, ISO dates, epoch integers) and clarifies the intent of `include_handled`, but `limit` and `course` receive no explanation, leaving half the parameters under-documented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific purpose: reporting what 'appeared, changed or vanished since `since`', a change-detection/diff view that is distinct from siblings like `history`, `backlog`, or `search`. Calling itself 'The main tool' signals its role as the primary entry point, though it does not explicitly name the siblings it is not.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'The main tool' implies this is the default starting point, and the 'what remains, not what happened' framing explains when the tool fits (current open/relevant changes vs. history). However, there are no explicit exclusions or redirects to alternatives such as `history`, `deadlines`, or `search`, so an agent must infer when to choose a sibling instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
12 tool updates
v0.1.0- First observed
backlog - First observed
course_tree - First observed
courses - First observed
deadlines - First observed
history - First observed
mark_handled - First observed
read_document - First observed
search - First observed
set_course_policy - First observed
sync - First observed
sync_status - First observed
whats_new
TDQS
Scored across 12 tools
The tools are mostly distinct in purpose—listing courses, diffing changes, showing deadlines, searching, reading, handling, and syncing. A few overlaps exist, such as whats_new also surfacing deadline changes and pending courses, and backlog overlapping with whats_new's unhandled view, but the descriptions are explicit enough to resolve ambiguity.
All names are lowercase and use underscores, but the convention is mixed: plural nouns (courses, deadlines), bare verbs (search, sync), and verb+noun compounds (read_document, mark_handled, set_course_policy) coexist. The names are readable but not predictable from a single pattern.
Twelve tools is well-scoped for a Moodle change-watching service, covering distinct phases from course policy and syncing to change discovery, reading, history, and handling. Each tool earns its place without obvious bloat or trivial additions.
The core workflow is covered end-to-end: sync, see what changed, inspect course structure and content, read files, mark items handled, and set course policy. Minor workarounds exist, such as no explicit undo of a handling action, but they do not block the main use case.
Maintenance
Related MCP Connectors
Versioned agent memory in your own Postgres: portable context, permissioned, audit trail.
- memnodeOAuthdev.memnode
Persistent, inspectable memory for AI agents with lineage, correction, and a hosted MCP endpoint.
Long-term memory for AI agents: bitemporal fact ledger, contradiction detection, explainability.
An agent-native database over MCP: shared, validated, structured records in every AI chat.
Related MCP Servers
- AlicenseAqualityAmaintenanceChange tracking for AI-era codebases. AI agents call it to log structured change events (entity + diff + reasoning) before the session ends, then query history with diff, blame, history, changeset, and search. Captures the intent that would otherwise evaporate.8433 PyPI23MIT
- AlicenseNot gradedqualityAmaintenanceProvides a local, queryable mirror of changelogs from Anthropic and related AI dev tools, enabling agents to discover cross-product workflows and sync updates via MCP.42 npmMIT
- AlicenseNot gradedqualityDmaintenanceMCP server for structured reflection and self-modeling over Everlog diary exports, enabling agents to query local diary evidence and maintain versioned artifacts of threads, moments, beliefs, and decisions.MIT

statecli-mcp-serverofficial
AlicenseNot gradedqualityDmaintenanceGives AI agents memory, undo, and self-awareness by tracking file changes and enabling checkpoints and rollbacks.36 npm2MIT