Moodle LMS for LLM agents — courses, assignments, files, submissions in one static binary.
A Model Context Protocol server that lets LLM agents use a real Moodle LMS — read courses, fetch assignment PDFs, write code, upload and submit — through a single static binary.
mcp-name: io.github.G36maid/moodle-mcp
Built in Rust on the official MCP SDK (rmcp).
Verified live against Moodle 3.7, 4.5 and 5.2; designed for 3.7 → 5.x.
A generic chatbot has no way into your Moodle, so every study task stays manual — five browser tabs to answer "what's due this week?", PDFs re-uploaded by hand, forums and grades copy-pasted one page at a time.
moodle-mcp signs the agent in as you. 20 typed tools cover the full student journey, and the agent chains them by itself — without ever opening a browser:
Needs a Moodle 3.7 → 5.x account on a site that allows the mobile-app web service (most do — see prerequisites).
# 1. Install
cargo install moodle-mcp # or a prebuilt binary / one-click .mcpb — no Rust needed
# 2. Log in once — prints MOODLE_URL and MOODLE_TOKEN
moodle-mcp auth
# 3. Connect your client (Claude Code shown; Codex/OpenCode/pi below)
claude mcp add moodle \
--env MOODLE_URL=https://moodle.example.com \
--env MOODLE_TOKEN=your-token \
-- /path/to/moodle-mcp
# 4. Open a chat and ask:
# "What's due in the next two weeks? Plan my studying around it."
Or paste this into your agent and let it do every step (it will open
moodle-mcp auth in its terminal — type your school credentials there):
Set up moodle-mcp (github.com/G36maid/moodle-mcp): read docs/src/installation.md
and docs/src/configuration.md from the repo's main branch first. Then install
the binary for my platform, run `moodle-mcp auth` in the terminal so I can
type my school credentials when it prompts, and register the server in your
own MCP config using the snippet for your harness. Never store or repeat my
credentials; ask before anything global.
Prebuilt binaries, SSO login, config snippets for every client, and the full environment-variable reference: Installation · Getting Started · Configuration
All of these work with no further setup — the agent discovers the tools from their descriptions and chains them itself:
| You say | The agent chains |
|---|---|
| "Finish my Information Security HW1: read the assignment brief and the attached PDF, work through the problems, and submit a draft." | get_assignment (brief + attachments) → fetch_file (PDF → markdown, math included) → solves it → writes the answer file → upload_file → submit_assignment → get_assignment (verify submitted ✓) |
| "Based on upcoming deadlines and course materials, build me a complete study plan for the next two weeks." | get_schedule (all deadlines) → get_course_overview per course → day-by-day plan balanced against due dates |
| "I missed what happened in Data Communication this week — catch me up, including the announcements forum." | get_notifications + get_forum_discussions → get_discussion_posts → synthesized weekly digest |
| "Prep me for the Software Engineering quiz: what did the teacher post about it?" | get_course_overview (quiz entry) → get_assignment (requirements) → related forum threads |
The tools mirror how you navigate Moodle — each level's output embeds the IDs for the next:
flowchart TD
Q["💬 prompt (intent only)"] --> LC["list_courses(search?)<br/>find the course"]
LC --> OV["get_course_overview(course_id)<br/>markdown map: sections · files ·<br/>assignments · quizzes · forums · grades"]
OV --> FD["get_forum_discussions(forum_id)<br/>→ get_discussion_posts(discussion_id)"]
OV --> AS["get_assignment(assignment_id)<br/>brief · attachments · submission · grade"]
AS --> FF["fetch_file(url)<br/>assignment PDF → readable markdown"]
FF --> WK["agent works:<br/>answers · code · report on disk"]
WK --> UP["upload_file(path, filename)<br/>solution → draft area → item_id"]
UP --> SB["submit_assignment(assignment_id, item_id?, finalize?)<br/>save draft or turn in"]
SB --> VF["get_assignment<br/>verify: submitted ✓ files ✓"]
OV --> SC["get_schedule(time window)<br/>deadlines → study plan"]
OV --> GR["get_grades(course_id)<br/>graded items + feedback"]
Everything the dedicated tools don't cover is reachable through the
call_ws_function + list_ws_functions escape hatch — full coverage of the
~350 functions a typical token can call (20 + 2 = 22 in total; the escape
hatch is opt-in via MOODLE_ENABLE_WS_FUNCTION=true).
Full documentation lives at moodle.ntnu.cc/docs (mdBook —
mdbook serve docs to read locally):
| Page | Contents |
|---|---|
| Getting Started | from zero to the first prompt; no MCP experience assumed |
| Installation | prebuilt binaries, cargo, from source |
| Configuration | every environment variable; config snippets for Claude Code, Codex, OpenCode, and pi |
| Tools | the full 22-tool reference and the ID flow between them |
| Security & Authentication | token lifecycle, transport and write safety |
| Changelog | release notes, version by version |
| Development | architecture tour, tests, release process |
| About | what moodle-mcp, MCP, and Moodle are |
cargo test # unit + wiremock integration + MCP protocol tests
cargo clippy --all-targets
cargo fmt -- --check
cargo build --release
Project documentation lives in docs/ — mdBook,
formatted with Prettier (see docs/README.md); preview with
mdbook serve docs.
The landing page lives in site/ — Astro 7 + TypeScript
(cd site && npm install && npm run dev, build with npm run build).
Use HTTPS — the token travels with every request. It never appears in tool output or errors, is only injected into same-site Moodle URLs, and write tools confirm with you before they act. Full details: Security & Authentication.
MIT — see LICENSE.
This listing does not have a supported local package template. Use the maintainer’s documentation for its hosted endpoint, authentication, and client-specific setup. No install command has been inferred.
io.github.G36maid/moodle-mcp works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.