moodle-mcp
Provides read-only access to a Moodle instance as a student, with tools for viewing deadlines, courses, course materials, assignments, forums, grades, searching files and content, and syncing course files locally.
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 assignments are due this week for my courses?"
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
A read-only MCP server that gives AI agents (Claude Code, Claude Desktop, claude.ai…) access to your EPFL Moodle as a student: deadlines, course files, assignments, forums, grades.
It's built for moodle.epfl.ch and for macOS, where it keeps your Moodle key in the Keychain. It isn't affiliated with EPFL or Moodle.
Tools
Tool | What it answers |
| What's due soon, and whether Moodle still expects something from you |
| Your courses (code, name, id) |
| Sections and items of a course, with ids |
| Any item: PDF text, assignment brief + submission status, page text, forum threads, folder files, links, quizzes |
| Text of an attachment or folder file (also saved to |
| Find items by name across courses (Moodle's global search is disabled at EPFL) |
| Full-text search inside course PDFs, code and pages; returns pages + snippets and the call to read just those pages |
| Recent posts in Announcements forums |
| A full forum thread |
| Grade items, scores and feedback |
| New/edited items, files, grades and forum posts since your last check (or |
| Download a course's files, attachments and pages to |
Courses accept CS-214, cs214, a numeric id, or part of the name.
Courses hidden on your Moodle dashboard are skipped unless named explicitly.
The tools only read from Moodle; sync_course writes only to your own disk.
Token use
Long PDFs open as a page map (one line per page, repeated slide builds merged into ranges,
plus the PDF outline if it has one); the agent then asks for pages="12-14". A 127-slide deck
costs ~1.4k tokens to map instead of ~9k to dump. search_in_files indexes a course's files on
first use (text cached in ~/.cache/moodle-mcp/files, refreshed when a file changes on Moodle),
so later searches are instant and return only the matching pages.
Related MCP server: Moodle MCP Server
Install
You need macOS and uv (it fetches Python 3.12+ itself). Clone this repository, then from its folder:
claude mcp add --scope user moodle -- "$(command -v uv)" --directory "$PWD" run moodle-mcpFor Claude Desktop (chat/Cowork), add the same command to mcpServers in
~/Library/Application Support/Claude/claude_desktop_config.json, with absolute paths:
{
"mcpServers": {
"moodle": {
"command": "/opt/homebrew/bin/uv",
"args": ["--directory", "/path/to/moodle-mcp", "run", "moodle-mcp"]
}
}
}Then install the key helper (see below) and ask your agent anything about Moodle. Since no key is stored yet, it answers with a link to get one.
sh scripts/install_url_handler.shThe Moodle key
The server uses a Moodle mobile web service token (the key the Moodle app uses), read from the
macOS Keychain item moodle-epfl or from $MOODLE_TOKEN. EPFL's Security keys page doesn't
hand one out, so it comes from the app sign-in flow: launch.php redirects a signed-in browser to
moodlemobile://token=…. Keys expire from time to time.
Getting or renewing it
When the key has expired, or none is stored yet, tools answer with renewal steps that the agent
walks you through. You never paste the key into the chat. With Moodle Key Helper installed
(scripts/install_url_handler.sh puts it in ~/Applications), it takes one link:
The local server makes up a random passport, saves it to
~/.cache/moodle-mcp/pending_renewal.json(valid 15 minutes) and gives youlaunch.php?…&passport=<it>.You open the link in a browser signed in to Moodle (if you aren't, Moodle carries on after sign-in) and let the browser open Moodle Key Helper. Moodle redirects to
moodlemobile://token=…, which holds the key signed with md5(site URL + passport).The helper runs
save_token.py --from-app. It accepts the link only if the signature matches the pending passport, checks the key with Moodle, stores it in the Keychain, sends it to your hosted server if you have one, and confirms with a notification. The running server picks it up on the next call.
Any web page can open a moodlemobile:// link, or send your browser to launch.php with a passport
of its own, but it can't know the pending one. So a page can't plant a key (for example, one for
another account), and links that arrive when no renewal is pending are ignored.
By hand
Without the helper, the renewal steps also give the manual route, with the same link:
Signed in to moodle.epfl.ch in Chrome, open DevTools → Network (tick Preserve log).
Open the link (any passport works here, e.g.
https://moodle.epfl.ch/admin/tool/mobile/launch.php?service=moodle_mobile_app&passport=12345&urlscheme=moodlemobile).Copy the
location: moodlemobile://token=…header of thelaunch.php303 response.python3 scripts/save_token.pyand paste it (input is hidden, and the key is never printed).
To revoke the key, reset the "Moodle mobile web service" key on moodle.epfl.ch → Preferences → Security keys, then get a fresh one.
On your phone, the web and cloud sessions (optional)
You can also host the server on fly.io and add it as a claude.ai custom connector. It then works in the Claude mobile apps, claude.ai, Claude Code on the web and routines. Connector calls come from Anthropic's servers, so cloud sandboxes don't need network access to it.
It's the same server with MOODLE_MCP_URL set, which switches it to HTTP behind a small OAuth
sign-in (oauth.py). Approving a connection takes a passphrase only you have, and sign-ins are
only handed back to Claude (claude.ai / claude.com) or to localhost. sync_course is left out
there, since it would save to the server's disk. The file cache, the whats_new marker and
sign-ins live on a volume.
Setup (once, with flyctl installed in ~/.fly/bin and logged in):
cp fly.example.toml fly.tomland pick an app name. SetMOODLE_MCP_URLto where it will be served:https://<app>.fly.dev, or your own domain (flyctl certs add).fly.tomlis ignored by git.flyctl apps create <app>andflyctl volumes create moodle_data --region cdg --size 1.sh scripts/setup_hosted.sh: creates the passphrase (Keychain itemmoodle-mcp-remote) and sends it and your Moodle key to fly as secrets, without printing either.flyctl deploy(also after code changes).On claude.ai: Settings → Connectors → Add custom connector, with your
MOODLE_MCP_URLfollowed by/mcp. When the sign-in page asks, paste the passphrase:security find-generic-password -s moodle-mcp-remote -w | pbcopy
When the Moodle key expires, renew it on your Mac: save_token.py also sends the new key to the
hosted server (the app in fly.toml), which restarts with it. The hosted server can't start a
renewal on your Mac, so it only gives the manual steps. For the one-link renewal, ask the local
server anything about Moodle instead: it uses the same key, so it gives you the link.
To disconnect every client: flyctl ssh console -C "rm /data/oauth.json", then flyctl apps restart <app>.
Configuration
Variable | Default | Purpose |
| – | Use this key instead of the Keychain (e.g. on the hosted server) |
|
| Keychain item holding the key |
|
| File cache, |
| – | Public URL; when set, serve over HTTP with OAuth sign-in |
| – | Passphrase that approves connections to the hosted server |
|
| HTTP port of the hosted server |
License
MIT, see LICENSE. PDF text extraction uses PyMuPDF, which is licensed under the AGPL-3.0 (or a commercial license from Artifex).
Available Tools
12 toolsannouncementsARead-only
Recent posts in the courses' Announcements (news) forums.
Args:
days: How far back to look.
course: Limit to one course (code like "CS-214", id, or name).
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| course | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the description does not need to restate safety. It adds some useful context by framing the result as 'Recent posts' and explaining the days window and course scoping, but it does not disclose ordering, limits, or how the open-world hint affects the returned posts. Overall it provides modest value beyond the annotations.
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 short sentences plus a bulleted argument list, with no filler. The purpose is front-loaded, and each line earns its place by adding either scope or parameter semantics.
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, read-only tool with two optional parameters, an output schema, and readOnly/openWorld annotations, the description is nearly complete. It covers what the tool returns, the time filter, and the course filter. It does not mention ordering or result limits, but the output schema likely captures return structure, so this is a minor 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 of explaining the parameters. It does this well: 'days: How far back to look' clarifies the time window, and 'course: Limit to one course (code like "CS-214", id, or name)' gives concrete accepted formats that the schema does not provide. It would benefit from specifying the unit for days more explicitly, but it is still genuinely helpful.
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 resource clearly: 'Recent posts in the courses' Announcements (news) forums.' It identifies exactly what the tool returns and distinguishes it from related tools like read_discussion or whats_new, though it does not explicitly name those alternatives. A more explicit verb like 'List' or 'Get' would make it stronger.
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 through the parameter documentation ('How far back to look', 'Limit to one course') but provides no explicit guidance on when to use this tool versus alternatives such as whats_new, read_discussion, or upcoming_deadlines. There are no exclusions or conditions directing the agent to a sibling tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
course_overviewARead-only
Show a course's sections and every item in them (files, assignments, pages, links, forums...) with ids.
Args:
course: Course code like "CS-214", numeric id, or part of the name.
section: Only show sections whose number or name matches (e.g. "3" or "Semantics").
| Name | Required | Description | Default |
|---|---|---|---|
| course | Yes | ||
| section | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the agent knows this is a safe read operation and may return unexpected/extra data. The description adds the behavior of listing all items in sections, which is useful. However, it doesn't disclose details like pagination, depth of item details, or whether the output is hierarchical. With annotations covering the safety profile, a 3 is appropriate.
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: the first sentence states the core function, and the Args section provides parameter details. Every sentence earns its place, with no fluff.
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 moderate complexity (2 params, output schema exists), the description is mostly complete. It explains what the tool does, how to specify the course, and how to filter sections. The output schema likely covers return values, so the description doesn't need to. A minor gap: no mention of what happens if the course is not found or if the section filter matches nothing, but this is not critical for a read-only overview 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 does: it explains the 'course' parameter accepts a code, numeric id, or name part, and the 'section' parameter filters by number or name with examples. This adds significant meaning beyond the bare schema. However, it doesn't specify the exact format of the output or how section matching works in edge cases (e.g., partial matches).
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 tool's function: showing a course's sections and every item in them, with specific examples of item types (files, assignments, pages, links, forums) and ids. This distinguishes it from siblings like list_courses (which likely lists courses) and open_item (which likely opens a specific item).
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 explains the purpose and the two parameters, including how to specify the course (code, numeric id, or name part) and how to filter sections. It doesn't explicitly state when to use this tool vs alternatives, but the clear scope (course overview vs. list_courses, open_item, etc.) implies the usage context. A small gap: no explicit 'use this instead of X when...' guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
gradesARead-only
Your grades in a course: every grade item with score, range, percentage and feedback.
Args:
course: Course code like "CS-214", numeric id, or part of the name.
| Name | Required | Description | Default |
|---|---|---|---|
| course | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint and openWorldHint annotations already cover the safety and non-exhaustiveness profile, so the description is not required to restate them. It adds the output-scope detail that all grade items are returned, but no behavioral quirks like pagination, freshness, or matching ambiguity are disclosed.
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 front-loaded: one clear summary sentence followed by a single documented arg. Every sentence carries information 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 simple read-only, single-parameter tool with an output schema and safety annotations, the description covers the input format and the returned content adequately. It omits only sibling-selection guidance, which is already penalized under usage guidelines, and ambiguity handling, which is minor for this level of 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?
With 0% schema coverage, the description fully compensates by defining the course parameter as a code, numeric id, or name fragment and giving the CS-214 example. This is materially more useful than the bare string property in the schema, though it doesn't address ambiguous matches.
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 resource (grades) and exactly what it returns: every grade item with score, range, percentage, and feedback, scoped to a course. That clearly conveys an information-retrieval purpose and separates it from announcements or deadlines by content, though it never uses an explicit verb like 'retrieve'.
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?
There is no guidance on when to use this tool versus siblings such as course_overview or list_courses, and no exclusions or alternatives are named. The only hint is the word 'grades', which weakly implies use when grade details are needed.
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 you're enrolled in, most recently visited first.
Courses hidden on your Moodle dashboard are ones you don't follow; other tools skip them
unless you name them explicitly.
Args:
include_past: Also list courses whose end date has passed.
include_hidden: Also list courses hidden on your dashboard.
| Name | Required | Description | Default |
|---|---|---|---|
| include_past | No | ||
| include_hidden | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true and openWorldHint=true. The description adds useful behavioral context: the result ordering (most recently visited first) and the meaning of hidden courses (ones you don't follow). It could mention more about pagination or response volume, but given the annotation coverage, it exceeds the baseline.
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 front-loaded with the primary purpose, followed by one relevant contextual note and a concise Args section. No sentence is wasted, and the structure makes the two optional parameters 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?
The tool is simple with two optional booleans and an output schema already present. The description covers ordering, hidden-course semantics, and both parameters. Nothing needed to safely invoke this tool correctly is missing.
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 bears the full burden of explaining parameters. It does so clearly: include_past ('Also list courses whose end date has passed') and include_hidden ('Also list courses hidden on your dashboard'). Each boolean's effect is explicit, and the word 'Also' implies the default false behavior matching the schema defaults.
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 verb ('List'), a clear resource ('courses you're enrolled in'), and a distinctive ordering ('most recently visited first'). It also distinguishes itself from siblings by explaining that hidden dashboard courses are skipped by other tools unless named explicitly, which helps disambiguate from course_overview and similar tools.
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 clear context about when this tool is relevant and explains hidden-course semantics relative to other tools ('other tools skip them unless you name them explicitly'). It does not explicitly name sibling alternatives or provide a direct when-to-use versus when-not-to-use comparison, but the context is strong enough for an agent to make a reasonable choice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_itemARead-only
Open any course item by id and return its content.
- file (resource): the extracted text. Long PDFs return a page map (one line per page)
first; then call again with pages="12-14" for just the pages you need
- assignment: instructions, dates, attachments, your submission status and grade
- page: its text
- forum: latest discussions (read one with read_discussion)
- folder: its files (read one with read_file)
- link (url): the target URL
- quiz: open/close dates, time limit, attempts, best grade
Args:
item_id: The [id ...] from course_overview, search or upcoming_deadlines.
pages: For PDFs, which pages to read, e.g. "1-5", "2,4,10-12" or "all".
max_chars: Maximum characters of file text to return; longer PDFs get a page map.
| Name | Required | Description | Default |
|---|---|---|---|
| pages | No | ||
| item_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?
The description discloses important runtime behavior beyond the readOnlyHint/openWorldHint annotations: long PDFs return a page map and require a second call with pages, and max_chars limits file text. It also explains what each item type returns, which is substantial behavioral context.
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 well-structured: a one-line summary, scannable item-type bullets, and an Args section. Every sentence adds useful information, and the long-PDF behavior is front-loaded where it matters most.
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 read-only opener with seven item types and three parameters, the description covers all relevant behaviors and return shapes. An output schema exists, so detailed return fields need not be repeated. No critical calling context is missing.
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 carry parameter meaning. It fully does: item_id is sourced from specific tool outputs, pages includes concrete format examples like '1-5' and '2,4,10-12', and max_chars is tied to truncation and page-map behavior.
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: 'Open any course item by id and return its content.' The item-type bullets further clarify what each kind of item yields, and references to read_file/read_discussion distinguish it from those sibling tools.
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 states where item_id comes from (course_overview, search, upcoming_deadlines) and gives explicit alternatives for drilling into specific items, e.g., 'read one with read_discussion' and 'read one with read_file.' This gives an agent clear routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_discussionARead-only
Read a whole forum thread (all posts, oldest first).
Args:
discussion_id: The [discussion ...] id from open_item on a forum, or from announcements.
| Name | Required | Description | Default |
|---|---|---|---|
| discussion_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adds useful behavioral context: it returns all posts and orders them oldest first. However, it does not describe pagination, response size limits, or other runtime behaviors. This is comparable to the get_calls calibration where the description adds scoping but not rich behavioral detail.
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: the first states the core behavior, the second documents the parameter source. Every sentence earns its place, and the purpose is front-loaded. No redundant 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 simple one-parameter read tool with readOnlyHint=true, openWorldHint=true, and an output schema present, the description covers what an agent needs: what the tool does, the ordering behavior, and where to obtain the discussion_id. No critical calling information is missing.
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 does: it explains that discussion_id comes from open_item on a forum or from announcements, which is meaningful context beyond the bare integer schema. It does not fully specify the id format, but the sourcing guidance is sufficient for a single parameter.
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: 'Read a whole forum thread (all posts, oldest first).' This clearly distinguishes it from siblings like open_item, which likely opens a single item, by emphasizing the full thread scope and ordering.
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 the whole thread is needed—but does not explicitly state alternatives or exclusions. It does provide useful sourcing guidance for the discussion_id, referencing open_item and announcements, which helps an agent locate the parameter value.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_fileARead-only
Download a Moodle file (e.g. an assignment attachment or a file in a folder) and return its text.
Long PDFs return a page map first; call again with pages="12-14" for the pages you need.
Code and text files come back as-is. On the user's Mac the file is also saved locally (path in the output).
Args:
file_url: A moodle.epfl.ch .../pluginfile.php/... URL from open_item, search_in_files or read_discussion.
pages: For PDFs, which pages to read, e.g. "1-5", "2,4,10-12" or "all".
max_chars: Maximum characters of text to return; longer PDFs get a page map.
| Name | Required | Description | Default |
|---|---|---|---|
| pages | No | ||
| file_url | 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?
Beyond the readOnlyHint=true annotation, the description discloses concrete behavior: PDFs get a page map for long files, code/text files return as-is, files are saved locally on the user's Mac with the path in output, and max_chars triggers page-map behavior. This is substantial behavioral transparency, exceeding what annotations provide.
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 front-loaded with the core action, then gives behaviorally important PDF handling, then parameter details. Every sentence provides useful information; there is no filler or repetition of schema structure.
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 an output schema is present, return values need not be explained. The description covers all three parameters, behavior for different file types, pagination flow, local saving, and URL provenance. An agent has everything needed to call the tool 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%, so the Args section carries the full burden. It explains file_url's expected URL shape, pages formats ('1-5', '2,4,10-12' or 'all'), and max_chars semantics. This compensates completely for the schema's lack of descriptions.
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: 'Download a Moodle file... and return its text.' It clarifies the kinds of files (assignment attachments, folder files) and sources for the URL, which distinguishes it from sibling tools like read_discussion or search_in_files.
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 clear operational guidance: long PDFs return a page map first, then instructs to call again with pages='12-14'. It also specifies where file_url comes from (open_item, search_in_files, read_discussion), providing workflow context. It doesn't explicitly state exclusions or when not to use this tool, but that is not a major gap given the clear file-reading purpose.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchARead-only
Find course items (files, assignments, pages, links...) and sections by name across your courses.
Matches words in item names, file names and section names (accents and case ignored).
Args:
query: Words to look for, e.g. "homework 3" or "lecture 5 slides".
course: Limit to one course; by default searches all ongoing courses.
| Name | Required | Description | Default |
|---|---|---|---|
| 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?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is known. The description adds useful behavioral details: matching ignores accents and case, and the default scope is 'all ongoing courses.' However, it does not disclose result pagination, ordering, or behavior when no matches are found, leaving some gaps beyond annotations.
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 well-structured. The first sentence states the primary purpose, followed by a one-line note on matching behavior, and then a clean Args section with examples. No fluff or redundancy; every sentence contributes value.
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, the description is reasonably complete. It covers the search scope, matching rules, and parameter defaults. It doesn't describe the return format, but since an output schema exists, that is not required. The tool's simplicity and annotations cover most operational context, making this a solid score.
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 fully explain the parameters. It provides concrete examples for 'query' (e.g., 'homework 3') and clarifies the 'course' parameter's default and scope (all ongoing courses). It adds meaning beyond the raw schema, though it does not specify the expected format of the course value (e.g., ID vs. name), which would be helpful.
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 verb and resource: 'Find course items ... and sections by name across your courses.' It distinguishes from siblings by explicitly limiting to name-based search, which contrasts with search_in_files (likely content search). The purpose is clear and 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?
The description implies when to use the tool (searching by name, ignoring accents/case, defaulting to ongoing courses) but does not explicitly mention alternatives or when not to use it. No comparison to search_in_files or other siblings is provided, leaving the agent to infer the appropriate context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_in_filesARead-only
Full-text search inside course files (PDF slides, lecture notes, handouts, code) and pages.
Returns the best-matching pages with a snippet and the exact call to read them, so you can
read only the relevant pages instead of whole files. The first search in a course downloads
and indexes its files (can take a minute); later searches are instant.
Args:
query: Words that must all appear on the same page, e.g. "big-step semantics".
course: Limit to one course (recommended); by default searches all ongoing courses.
max_results: How many matching pages to return.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| course | No | ||
| max_results | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint and openWorldHint annotations, the description discloses meaningful behavioral details: the first search downloads and indexes files with a latency cost, later searches are instant, and query words must all appear on the same page. This adds real operational context without contradicting the annotations.
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 well-structured and front-loaded: the core behavior, return value, and cost are stated first, followed by concise parameter explanations. Every sentence carries information useful for selecting or invoking the tool, 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 search tool with an output schema, the description covers all essential calling context: scope, result shape, indexing tradeoff, and parameter semantics. Nothing an agent needs to decide whether to call it or how to call it is missing.
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?
Although the schema has 0% description coverage, the description fully compensates by explaining each parameter: query requires all words on the same page, course limits scope and defaults to all ongoing courses, and max_results controls the number of returned pages. It adds practical meaning and an example that the schema alone lacks.
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 is explicit and specific: it states a clear verb ('full-text search'), a concrete resource ('course files ... and pages'), and the key output (matching pages with a snippet and an exact call to read them). This clearly differentiates it from a generic 'search' tool and from reading whole files.
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 conveys when to use the tool by explaining that it lets the agent read only relevant pages instead of whole files, and recommends limiting to one course. It does not explicitly name alternative tools or state when not to use it, but the context is clear enough for an agent to route correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sync_courseAIdempotent
Download a course's materials into a local folder, one subfolder per section.
Saves files, folder contents, assignment attachments and pages (as .md), plus an INDEX.md
listing everything with links, due dates and item ids. Only new or changed files are
downloaded, so it's cheap to run again. Nothing on Moodle is changed.
Args:
course: Course code like "CS-214", numeric id, or part of the name.
folder: Where to save. Defaults to ~/Moodle/<course code>.
max_file_mb: Skip files larger than this (e.g. lecture videos).
| Name | Required | Description | Default |
|---|---|---|---|
| course | Yes | ||
| folder | No | ||
| max_file_mb | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses rich behavioral details beyond the annotations: it writes to a local folder, creates one subfolder per section, produces INDEX.md with links/due dates/item ids, downloads only new or changed files, skips files over a size limit, and explicitly states nothing on Moodle is changed. The annotations (idempotent, non-destructive, not read-only) are consistent with this local-write behavior, so there is no contradiction.
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 front-loaded: the core behavior is in the first sentence, followed by a concise behavior summary and a short Args block. Every sentence earns its place; there is no filler or repetition of schema metadata.
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 tool with three parameters and an output schema, the description covers all essential operational aspects: what gets downloaded, where it goes, how re-runs behave, safety, and parameter meaning. The presence of the output schema means not explaining return values is acceptable.
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 parameters. It compensates excellently: course is explained with accepted forms ('CS-214', numeric id, or part of the name), folder gives the default path pattern, and max_file_mb is described with a concrete example ('lecture videos'). This meaningfully exceeds the bare type information in the 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 opens with a specific verb and resource: 'Download a course's materials into a local folder, one subfolder per section.' This clearly distinguishes it from sibling tools like read_file, search, and course_overview, all of which serve different purposes. No ambiguity about what this tool does.
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 intended use case (getting a local copy of course materials) is clear from the purpose itself, and the description adds context that it is cheap to re-run and changes nothing on Moodle, implying repeated safe use. However, it does not explicitly contrast with alternatives or state when not to use it, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
upcoming_deadlinesARead-only
What's due soon: assignment/quiz deadlines plus other calendar events, with whether you still need to act.
"TODO" means Moodle still expects something from you (e.g. nothing or only a draft submitted);
"done" means it doesn't (already submitted, or nothing to hand in).
Args:
days: How many days ahead to look.
course: Limit to one course (code like "CS-214", id, or name).
include_past_days: Also show deadlines from the last N days (to catch overdue work).
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | ||
| course | No | ||
| include_past_days | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations include readOnlyHint and openWorldHint, and the description adds valuable behavioral context beyond that: it defines the 'TODO'/'done' status meaning and notes that include_past_days helps catch overdue work. This clarifies the tool's output semantics without contradicting the annotations.
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 well-structured: a lead sentence stating the purpose, a short clarification of the status flag, then a compact Args list. It avoids redundancy and front-loads the core functionality.
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 description covers all parameters, explains the output's status semantics, and mentions a useful use case (catching overdue work). With an output schema present, it does not need to detail return values. It is sufficiently complete for an agent to invoke the tool 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%, so the description is the sole source of parameter meaning. It explains 'days' as how far ahead to look, 'course' as a filter by code/ID/name, and 'include_past_days' as a way to include recent past deadlines. This fully compensates for the missing schema descriptions.
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 tool's function: listing upcoming deadlines from assignments/quizzes and calendar events, plus a status indicator of whether action is needed. It also explains the 'TODO' vs 'done' semantics, making it distinct from siblings like 'course_overview' or 'whats_new'.
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 explains how to use the arguments and what the tool does, but it does not explicitly state when to use this tool over alternatives or provide exclusion criteria. The intended use case (checking deadlines) is implied but not contrasted with sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
whats_newARead-only
What changed in your courses: new or edited items and files, new grades, new forum posts.
By default covers the time since you last called whats_new without `since` (or the last
7 days the first time), then remembers now as the new "last check".
Args:
since: Look back this far instead: "3d", "12h", "2w", or a date like "2026-09-20".
Doesn't move the "last check" marker.
course: Limit to one course (code like "CS-214", id, or name).
| Name | Required | Description | Default |
|---|---|---|---|
| since | No | ||
| course | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses a non-obvious stateful behavior: the tool remembers the last call time and uses it as the default window, and that passing `since` does not update that marker. This goes well beyond the readOnlyHint and openWorldHint annotations, which only indicate safety and that results may vary. The description also clarifies the first-time fallback, which is exactly the kind of behavioral context an agent needs.
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 front-loaded: the first sentence states the purpose, the second explains the default behavior, and the Args section is terse and scannable. Every sentence earns its place, and the parameter documentation is formatted for quick parsing.
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 description is complete for a read-only, two-parameter tool with an output schema present. It explains the stateful default, the parameter semantics, and the scope of results. The only thing not covered is the exact shape of the output, but the output schema exists and the description doesn't need to duplicate it. A small gap is that it doesn't mention whether results are sorted or limited, but that's minor given 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?
Schema description coverage is 0%, so the description carries the full burden for parameter meaning. It does this well: `since` gets format examples and the semantic distinction of not moving the marker, and `course` gets accepted formats (code, id, or name) with an example. The only minor gap is that it doesn't specify what happens if an invalid course is provided, but that's not essential for invoking the tool correctly.
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 ('what changed') and enumerates the exact resource types covered: new or edited items and files, new grades, new forum posts. This clearly distinguishes it from siblings like announcements, grades, or read_discussion, which focus on single resource types.
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 explains the default time window behavior and how the 'last check' marker works, including the first-call fallback of 7 days. It also documents the two optional parameters with concrete examples ('3d', '12h', '2w', or a date like '2026-09-20'), and clarifies that using `since` does not move the marker. This is strong when-to-use guidance.
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
announcements - First observed
course_overview - First observed
grades - First observed
list_courses - First observed
open_item - First observed
read_discussion - First observed
read_file - First observed
search - First observed
search_in_files - First observed
sync_course - First observed
upcoming_deadlines - First observed
whats_new
TDQS
Scored across 12 tools
Most tools have clearly distinct purposes: listing courses, deadlines, course content, files, discussions, announcements, grades, search, and sync. However, open_item and read_file both handle file content, and search vs search_in_files could be confused (name search vs full-text search), though descriptions clarify the difference.
Tool names mostly follow a verb_noun pattern (list_courses, open_item, read_file, read_discussion, sync_course). Minor deviations: 'search' and 'search_in_files' are less consistent than 'search_items'/'search_files', and 'whats_new' lacks an underscore, but overall the pattern is readable and predictable.
12 tools is well-scoped for a Moodle assistant covering course browsing, deadlines, content retrieval, search, grades, announcements, and offline sync. Each tool serves a distinct workflow need without redundancy.
The surface covers the main student workflows: listing courses, viewing deadlines, reading content, searching, checking grades, and syncing. Minor gaps: no tool to submit assignments, create forum posts, or manage calendar events, but these are likely outside the server's read-oriented purpose.
Maintenance
Related MCP Connectors
Read-only access to Epivo's live course catalogue for AI agents.
- uNotesOAuthnet.unotes
Search university course materials, your flashcards, quizzes, streak and quota. All tools read-only.
Read-only bank access for your AI agent. Connects Claude, ChatGPT, Cursor, Gemini, Codex.
Direct access to 60+ scraping and search tools. Extract structured data from Google (Search, Maps, Trends), Amazon, Airbnb, Social Media, and any web page directly into your AI agent.
Related MCP Servers
- FlicenseAqualityCmaintenanceEnables read-only querying of Moodle as a student, including courses, assignments, grades, forums, and files, using a personal web services token.11-
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with Moodle LMS via the Moodle REST API, supporting management of courses, users, enrollments, grades, and content.GPL 3.0
- FlicenseNot gradedqualityBmaintenanceEnables AI assistants to read your Moodle courses, list materials, quizzes, and search content to plan exam preparation through natural language.1-
- FlicenseAqualityCmaintenanceEnables AI assistants to query the UTN distance learning Moodle campus, providing tools to list courses, view content, check deadlines, see grades, and more.7-