moodle-mcp
Integrates with Moodle using a personal Moodle mobile web service token to read course data. Provides tools for listing courses, getting course outlines, deadlines, announcements and forum discussions, checking and marking content updates as seen, reading files as text, downloading selected files, and mirroring a course's Moodle file structure locally. The integration is read-only and does not submit, post, or modify anything in Moodle.
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-mcpWhat do I have to hand in this week?"
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-mcp
Let your AI assistant read your Moodle courses. Ask things like:
What do I have to hand in this week? Anything new on Moodle since last time? Summarize the latest slides of INFO-F403. Download the new exercise sheets of the crypto course into my OneDrive course folder.
moodle-mcp is an MCP server: a small program that AI apps (Claude Code,
Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf…) launch in the background to get extra tools. It talks to
Moodle with your own token, through the same API as the official Moodle mobile app, and only reads.
Made for the ULB (uv.ulb.ac.be, the default), but it works with any Moodle site that allows the mobile app.
Install (5 minutes)
1. Install uv (a Python tool installer). Skip this step if you already have it.
macOS / Linux:
curl -LsSf https://astral.sh/uv/install.sh | shWindows (PowerShell):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Then open a new terminal.
2. Install moodle-mcp
uv tool install git+https://github.com/Nicolas-Prn/moodle-mcp3. Run the setup
moodle-mcp setupIt asks for your Moodle token (see below), checks it, then offers to register the server in every AI app it finds on your computer. Restart Claude Desktop (or other open apps) afterwards. That's it.
Getting your Moodle token
The token gives read access to your courses, as the Moodle app has. Keep it private.
Option A: Security keys page (easiest)
Log in to Moodle in your browser.
Open https://uv.ulb.ac.be/user/managetoken.php (profile picture → Preferences → Security keys).
Copy the key in the Moodle mobile web service row and paste it into
moodle-mcp setup.
Option B: if there is no such page or row
Log in to Moodle in Chrome, Edge or Firefox.
Open the developer tools (F12, or Cmd+Option+I on a Mac) and select the Network tab.
In the same tab, go to
https://uv.ulb.ac.be/admin/tool/mobile/launch.php?service=moodle_mobile_app&passport=1&urlscheme=moodlemobileClick the
launch.phprequest (it shows as failed, that's expected). Under Response Headers, copy the wholelocationvalue (moodlemobile://token=…) and paste it intomoodle-mcp setup, which decodes it.
For another university, replace uv.ulb.ac.be with your Moodle address and run moodle-mcp setup --url <address>.
Related MCP server: Moodle Student MCP
Using it
Just ask your assistant in plain language, in any conversation and any folder. It picks the right tools:
Tool | What it does |
| Your courses (current ones, or past ones too) |
| Sections, activities, links and files of a course, with file references |
| Upcoming and overdue things to do (assignments, quizzes…) |
| Recent announcements and forum posts, across all courses or one |
| A full forum thread |
| What is new / changed / removed since you last marked a course as seen |
| Marks the current content as seen (the assistant does it once you've dealt with the changes) |
| Reads a file as text so the assistant can explain or summarize it (PDF, Word, PowerPoint, HTML, code…) |
| Saves chosen files into a folder you name, optionally renamed or sorted into sub-folders |
| Saves a whole course, mirroring its Moodle structure ( |
A course can be named by its code (F403, info-h417), part of its name, or its id.
Downloads never overwrite a file that already exists, so annotating or editing downloaded files is safe.
When Moodle has a newer version of a file you already have, the assistant can save it next to yours as
name (2).pdf. check_updates compares Moodle with what it saw last time, not with your local files.
Supported apps
moodle-mcp setup (or moodle-mcp install later) registers the server automatically in:
Claude Code (all projects), Claude Desktop, Codex, Cursor, Gemini CLI and Windsurf.
For any other app that supports local (stdio) MCP servers, add a server with the command that
moodle-mcp status prints, for example:
{ "mcpServers": { "moodle": { "command": "/Users/you/.local/bin/moodle-mcp", "args": ["serve"] } } }Use the full path: desktop apps do not see your terminal's PATH.
Not supported yet: ChatGPT and the claude.ai website or mobile app. They can only reach MCP servers hosted on the internet, while this one runs on your computer (which is also why your token never leaves it).
Commands
moodle-mcp setup # save or replace your token, then register in your AI apps
moodle-mcp status # check the token and see which apps were detected
moodle-mcp install [apps...] # register again (e.g. after installing a new app): claude-code, claude-desktop, codex, cursor, gemini, windsurf
moodle-mcp uninstall # unregister from the apps
uv tool upgrade moodle-mcp # update to the latest versionTo remove everything: moodle-mcp uninstall, uv tool uninstall moodle-mcp, then delete ~/.config/moodle-mcp.
Privacy and security
The token is stored only on your computer, in
~/.config/moodle-mcp/config.json(readable by you only). App configurations contain just the command, never the token. You can also provide it through theMOODLE_TOKEN(andMOODLE_URL) environment variables instead.The server never changes anything on Moodle: no submissions, no posts, no messages.
The token is only ever sent to your Moodle site, and is removed from error messages.
Forum posts, descriptions and files are written by other people. The server tells the assistant to treat them as information, not as instructions.
To revoke access, use Reset on the Security keys page, then run
moodle-mcp setup --new-token.
Troubleshooting
The assistant doesn't see the Moodle tools: restart the app, then run
moodle-mcp status. In Claude Code,/mcplists connected servers."invalidtoken" or "No Moodle token configured": run
moodle-mcp setup --new-token.A big
mirror_coursetimes out: some apps stop waiting after a minute or two. Downloads already finished are kept, so asking again resumes where it stopped.
Development
git clone https://github.com/Nicolas-Prn/moodle-mcp && cd moodle-mcp
uv sync
uv run pytest
uv run moodle-mcp status
npx @modelcontextprotocol/inspector uv run moodle-mcp serve # try the tools in a web UICode layout (src/moodle_mcp/): server.py defines the MCP tools, courses.py the course and file model
(including file references), client.py the Moodle Web Service client, updates.py change detection,
downloads.py saving files, text.py text extraction, config.py settings and cli.py the commands.
License: MIT.
Available Tools
10 toolscheck_updatesARead-only
What is new, changed or removed on Moodle since the courses were last marked as seen.
The first call for a course records a baseline (everything currently there counts as seen).
| Name | Required | Description | Default |
|---|---|---|---|
| course | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint, so the description earns credit by disclosing the stateful baseline behavior: the first call per course silently records a baseline and returns nothing useful. That is non-obvious and important for correct invocation. It still doesn't say whether subsequent calls return a diff list or counts.
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 tight sentences, front-loaded with the purpose and followed by the one behavioral fact an agent must know. 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 single-parameter, read-only, open-world tool with no output schema, the description covers the core semantics and the baseline side effect. The remaining gap is the scope of the omitted-course default, which the richer plural framing muddies rather than resolves.
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?
Only one parameter ('course') with 0% schema description coverage and a null default. The description implies per-course scoping ('the first call for a course'), but never explains what happens when 'course' is omitted, despite the first sentence's plural 'courses' framing.
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 a specific outcome: what is new, changed or removed on Moodle relative to the last-seen state. This clearly separates it from mutation siblings like mark_updates_seen, though it never names that sibling or any other alternative explicitly.
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 baseline sentence implies usage context (call this to discover changes, then mark them seen), but the pairing with mark_updates_seen is left to inference and there is no explicit when-to-use/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
download_filesAIdempotent
Download course files into a local folder. Existing files are never overwritten.
- files: refs from get_course_outline / get_announcements / check_updates (or Moodle pluginfile URLs).
- dest_dir: absolute local folder (or ~/...), created if needed.
- save_as: optional {ref: "relative/path/name.pdf"} to choose where each file goes inside dest_dir;
by default a file keeps its Moodle name (and its sub-folder, for files inside folder modules).
- on_conflict: "skip" (default) leaves an existing file alone; "rename" saves the new one as "name (2).ext".
| Name | Required | Description | Default |
|---|---|---|---|
| files | Yes | ||
| course | Yes | ||
| save_as | No | ||
| dest_dir | Yes | ||
| on_conflict | No | skip |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses behavior the annotations cannot express: dest_dir is created if missing, nested folder-module structure is preserved by default, and the exact conflict policy (skip vs. rename to 'name (2).ext'). This usefully extends the annotations' idempotentHint/destructiveHint rather than repeating them.
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?
Front-loaded with purpose and the overwrite guarantee, then a tight bullet list where each entry maps to a parameter or decision. Slightly dense but no filler sentences.
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 write-to-disk tool with no output schema, the description covers naming, destination handling, and conflict resolution well. Missing only the meaning of the required 'course' argument and any mention of how download results/errors are reported.
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 carries the full burden, and it documents four of the five parameters (files, dest_dir, save_as, on_conflict) with formats, defaults, and examples. The required 'course' parameter is left completely unexplained, which is the one real 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 first sentence gives a specific verb and resource ('Download course files into a local folder') plus an immediate behavioral fact ('existing files are never overwritten'). It does not, however, distinguish itself from siblings that also touch files, notably read_file and mirror_course, so an agent still has to infer the boundary.
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 'files' bullet tells the agent exactly where the input refs come from (get_course_outline / get_announcements / check_updates), which is real routing guidance. It stops short of when-not to use it or naming mirror_course as the whole-course alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_announcementsARead-only
Recent forum discussions (announcements and other course forums), newest first, with attachment refs.
Messages are shortened; use read_discussion for a full thread.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| limit | No | ||
| course | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, so safety is covered; the description adds genuinely useful behavioral context that annotations cannot: results are truncated/shortened, ordered newest-first, and carry attachment refs rather than content. It does not disclose pagination behavior or the effective lookback window, which keeps it off a 5.
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 short sentences, fully front-loaded: what you get first, the escape hatch to read_discussion second. No filler and nothing repeated from the schema or annotations.
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?
There is no output schema, and the description does cover the shape of the return (shortened messages, ordering, attachment refs), which is the main thing it needed to do. However, with three parameters at 0% schema coverage and no annotation coverage of them, an agent still lacks the information required to set days/limit/course correctly.
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% — days, limit, and course carry no descriptions in the schema — and the description never mentions any of the three parameters. An agent cannot tell whether 'days' is a lookback window, what format 'course' takes (ID vs name), or how 'limit' interacts with truncation, so the description fails to compensate for the coverage 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?
States a concrete resource (recent forum discussions), clarifies the scope extends beyond announcements to other course forums, and specifies ordering (newest first) plus attachment refs. It also names the sibling it is not — read_discussion for full threads — so an agent can separate the two without opening a schema.
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?
Explicitly routes the full-thread case to read_discussion, which is the key decision point between these two siblings. It does not address overlap with check_updates or state any exclusion on the primary 'recent scan' use case, so it falls short of a full when/when-not treatment.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_course_outlineARead-only
Sections, activities, links and files of a course, with the file refs used by read_file/download_files.
Modules with many files are truncated; pass module_id to list every file of one module.
| Name | Required | Description | Default |
|---|---|---|---|
| course | Yes | ||
| module_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint, so the description adds real value by disclosing the truncation behavior for file-heavy modules and the module_id escape hatch. It does not say whether truncation is flagged in the response, which is the one gap.
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 sentences, front-loaded with what is returned and followed immediately by the truncation caveat. Nothing is wasted, though the telegraphic noun-phrase opening is slightly less scannable than an explicit verb statement.
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?
With no output schema, the description does the work of describing return values (sections, activities, links, files, file refs) and a key limitation (truncation). The remaining hole is the meaning/format of the required 'course' argument.
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 coverage is 0%, so the description must carry both parameters. It explains module_id well (pass it to list every file of one module), but the required 'course' parameter is left entirely undefined — no indication whether it takes an ID, slug, or name. Partial compensation justifies a 3.
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?
Names a specific resource (course outline) and enumerates its contents: sections, activities, links, files, plus the file refs that feed read_file/download_files. That cross-reference distinguishes it from siblings like list_courses and read_file. It stops short of a clean verb+resource statement, but an agent can tell what it returns.
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 implies a workflow role by tying the returned file refs to read_file/download_files, and it gives a concrete workaround for truncated modules via module_id. However, it never says when to prefer this over list_courses or mirror_course, and gives no prerequisites or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_deadlinesCRead-only
Upcoming deadlines and actions to do (assignments, quizzes...), including overdue ones from the last 2 weeks.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| course | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds real scope detail — overdue items are included but capped at the last 2 weeks — which is behavior beyond the annotations. It says nothing about return shape, ordering, or how the days window interacts with the fixed 2-week lookback.
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?
A single front-loaded sentence with no filler; the parenthetical examples are the only mild redundancy. Efficient and easy to scan.
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?
With no output schema and 0% parameter coverage, the description should explain what the two parameters do and roughly what is returned. It gives a rough sense of contents but leaves both parameters and the returned fields undocumented, so an agent cannot call it precisely.
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 carries the full burden for the two parameters and largely fails: 'days' (default 30) is never explained, and 'course' (nullable filter) is never mentioned. The 'last 2 weeks' phrase relates to a fixed overdue window, not to the days parameter, so an agent gets no help sizing the query.
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?
Names a concrete resource (deadlines/actions to do) with examples (assignments, quizzes) and states the time scope, so an agent can distinguish it from list_courses or get_announcements without opening a schema. It is clear but does not explicitly contrast itself with any sibling tool.
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 on when to call this versus get_announcements, check_updates, or get_course_outline, and no mention of how the days/course parameters change the scope. Usage can only be inferred from the phrase 'upcoming deadlines'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_coursesARead-only
List the courses the user is enrolled in (id, code, name). Past courses are hidden unless include_past.
| Name | Required | Description | Default |
|---|---|---|---|
| include_past | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds a genuinely useful behavioral fact beyond the schema: results are silently filtered to current courses unless include_past is set, which an agent could not infer from default=false alone. Return format and volume/pagination are still unaddressed.
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?
One sentence, front-loaded with the core action and followed by the output shape and the sole caveat. Nothing is wasted and nothing essential is deferred.
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, annotation-covered read tool with one parameter and no output schema, the description supplies purpose, returned fields, and the default filtering behavior. It stops just short of describing ordering, pagination, or what happens with zero enrollments.
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% for the single parameter, so the description carries the burden, and it does: it explains that include_past controls whether historical enrollments appear. That is the full semantic of the only parameter, though no format or edge-case detail is given.
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 a specific verb and resource ('List the courses the user is enrolled in') and even names the returned fields (id, code, name). It is clearly distinct from siblings like get_announcements or get_deadlines, though it never explicitly contrasts with the closest sibling get_course_outline.
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?
Usage is implied by the name and the enrollment framing, and the note about past courses being hidden gives one behavioral condition. However, there is no explicit 'use this when' guidance and no mention of alternatives such as get_course_outline for a deeper view of a single course.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_updates_seenAIdempotent
Record the current Moodle content as seen, so check_updates stops reporting it. Only touches local state.
| Name | Required | Description | Default |
|---|---|---|---|
| course | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare idempotentHint=true, destructiveHint=false and readOnlyHint=false; the description adds the meaningful clarification that it 'only touches local state', i.e., it does not modify Moodle content, which is real context beyond the annotations. The tension with openWorldHint=true is minor and openWorldHint semantics are debatable, but the description does not mention return behavior.
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 short, front-loaded sentences; the purpose leads and the side-effect clarification follows. Nothing is wasted.
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 low-complexity, no-output tool with annotations covering the safety profile, the description covers purpose, effect and locality. The only real gap is the unaddressed 'course' parameter's scoping behavior.
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% for the single optional 'course' parameter, and the description never mentions it or explains what omitting it does (all courses vs current course). The description provides no parameter-level meaning to compensate.
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 a specific verb and resource ('Record the current Moodle content as seen') and explicitly describes the effect on a named sibling ('so check_updates stops reporting it'), letting an agent separate it from check_updates and mirror_course without reading schemas.
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?
Clear context for when to call it: after reviewing content, to clear what check_updates reports. It names the dependent sibling and its effect, but offers no explicit when-not or alternative (e.g., what to do for a single course vs all).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mirror_courseAIdempotent
Download every file of a course into dest_dir, mirroring Moodle's structure. Never overwrites.
Layout: `<NN section>/<file>` for single-file resources, `<NN section>/<activity>/<file>` otherwise
(folders, multi-file resources, assignment statements), `Forum: <name>/<discussion>/` for attachments.
Use dry_run=True first on large courses to see what would be downloaded and how much.
| Name | Required | Description | Default |
|---|---|---|---|
| course | Yes | ||
| dry_run | No | ||
| dest_dir | Yes | ||
| include_forums | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the safety profile (readOnlyHint=false, idempotentHint=true, destructiveHint=false), and the description usefully adds 'Never overwrites' plus the exact on-disk layout, which is behavior an agent cannot derive from the schema. It does not say what happens on partial failures or whether existing files are skipped silently.
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 tight sentences: purpose first, then the layout contract, then the dry_run tip. The layout block is the only elaboration and it directly serves correct invocation; nothing is redundant with the schema.
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 write tool with no output schema, the description covers the core purpose and filesystem layout well but leaves include_forums and the course identifier format entirely undocumented. An agent could call it successfully for the defaults but would have to guess about forum inclusion.
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 none of the four parameters are documented in the description itself. dest_dir and dry_run are only indirectly implied, while include_forums (default false) and course are never explained — notably the layout mentions 'Forum: <name>/<discussion>/' although forums are excluded by default, which an agent could misread.
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 a concrete verb+resource+target ('Download every file of a course into dest_dir, mirroring Moodle's structure'), which clearly separates it from the flat download_files sibling via the directory-mirroring behavior. It never names that sibling or explicitly contrasts with it, so sibling differentiation is implicit rather than stated.
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 only guidance is 'Use dry_run=True first on large courses to see what would be downloaded and how much,' which is a workflow tip rather than when-to-use-this-vs-alternatives guidance. There is no statement of when not to use it, nor any routing to download_files or read_file for single-file needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_discussionBRead-only
Every post of a forum discussion (ids come from get_announcements).
| Name | Required | Description | Default |
|---|---|---|---|
| discussion_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered by structured data. The description adds no behavioral context at all: no pagination behavior, no error handling for an invalid or inaccessible discussion_id, no auth requirements, and no indication of how much content is returned.
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?
A single short sentence with no filler, and the scope statement comes before the parenthetical hint about parameter sourcing. It is slightly under-specified rather than padded, so nothing wasted but little provided.
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 phrase 'every post' communicates that the full discussion is returned without filtering, which covers the return-shape question in the absence of an output schema, and annotations handle the safety profile. Auth/permission expectations are the only real gap for a tool this simple.
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% for the single parameter, yet the description supplies the one fact the schema omits: where discussion_id values originate (get_announcements). That provenance note is the key semantic need for this tool, though it doesn't clarify the expected id format.
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 names a specific resource and scope: all posts of a single forum discussion, which is distinct from sibling reads like read_file or get_course_outline. It lacks an explicit verb, reading as a fragment, but the resource and scope are unambiguous enough for an agent to select it.
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 parenthetical '(ids come from get_announcements)' tells the agent how to obtain the required parameter, which is real usage guidance, but it never states when to prefer this tool over siblings such as read_file or get_course_outline, nor any precondition beyond the id source.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_fileARead-only
Text content of a course file (PDF, Word, PowerPoint, HTML, code, plain text).
`file` is a ref from get_course_outline/get_announcements (or a Moodle pluginfile URL).
`pages` selects PDF pages or PowerPoint slides, e.g. "1-5" or "2,7-9".
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | ||
| pages | No | ||
| course | Yes | ||
| max_chars | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds useful capability context (which file formats yield text, that pages apply to PDF/PPT), but says nothing about truncation behavior tied to max_chars (default 60000) or what happens when a document exceeds it.
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?
Very short and front-loaded: the capability comes first, then per-parameter notes. Every sentence earns its place, with only the opening noun phrase being slightly terse rather than a full verb statement.
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?
With no output schema, the description correctly focuses on what comes back ('text content') and format applicability. For a 4-parameter read tool it covers the operation adequately, though the unexplained max_chars/course and unstated truncation caveat leave a gap.
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 carries the full burden. It explains 'file' (ref source) and 'pages' with a concrete syntax example ('1-5', '2,7-9'), but leaves 'course' and 'max_chars' entirely undocumented in both schema and description, so half the parameters remain opaque.
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 names the concrete resource and scope — extracting text content from a course file — and enumerates supported formats (PDF, Word, PowerPoint, HTML, code, plain text), so an agent can tell it retrieves readable text. It does not explicitly contrast with the sibling download_files, but the operation is unambiguous.
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 gives provenance guidance ('file is a ref from get_course_outline/get_announcements') which tells the agent where to get the required input, a real usage hint. However, there is no when-not guidance and no mention of the alternative download_files for retrieving the raw file rather than its text.
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.
10 tool updates
v0.1.0- First observed
check_updates - First observed
download_files - First observed
get_announcements - First observed
get_course_outline - First observed
get_deadlines - First observed
list_courses - First observed
mark_updates_seen - First observed
mirror_course - First observed
read_discussion - First observed
read_file
TDQS
Scored across 10 tools
Tools are mostly distinct, but get_announcements and check_updates both surface new course content, and download_files and mirror_course both download files; descriptions clarify boundaries, but an agent could occasionally misselect.
All tool names use a consistent snake_case verb_noun pattern (get_, list_, read_, check_, mark_, download_, mirror_), making the set predictable and easy to scan.
10 tools is well-scoped for a Moodle course-access client, with no redundant or trivial tools; each covers a distinct capability.
The surface covers core read workflows (courses, outline, deadlines, announcements, discussions, files, updates, mirroring), but lacks search, grades, or assignment interaction, which are minor gaps for a read-only client.
Maintenance
Related MCP Connectors
Read-only access to Epivo's live course catalogue for AI agents.
- OpenOakOAuthorg.openoak
Secure AI access to OpenOak tasks, notes, and Kanban boards.
Search university course materials, your flashcards, quizzes, streak and quota. All tools read-only.
Read-only access to your Citlyze workspace: AI search visibility, citations, and recommendations.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables AI assistants to interact with Moodle via web services, allowing tasks like listing courses, assignments, events, and downloading files.1048 PyPI4MIT
- AlicenseAqualityBmaintenanceEnables read-only querying of Moodle as a student, including courses, assignments, grades, forums, and files, using a personal web services token.11397 npmMIT
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to read your Moodle courses, list materials, quizzes, and search content to plan exam preparation through natural language.1-
- AlicenseNot gradedqualityBmaintenanceEnables Claude to interact with Moodle via the official REST API for read-only tasks like listing courses, materials, assignments, submissions, and grades. Uses token-based authentication and emphasizes safe, no-write operations.2MIT