mcp-moodle-teacher
# mcp-moodle-staff
An MCP server that gives an AI assistant the staff side of Moodle — for the
lecturer marking their own course and for the academic office running a whole
campus: who is enrolled, who submitted, who has been absent, which registers are
late, which students are struggling in more than one course, the week's rooms,
and the lecturers' grade files turned into gradebook imports.
The Moodle MCP servers published so far are written from the student's seat:
*my* courses, *my* grades, *my* deadlines. This one is for the people on the
other side of the desk. Read-first; every tool that changes Moodle says so and
asks for confirmation.
Tested against Moodle 4.5 with the standard `moodle_mobile_app` web service.
**Not comfortable with a terminal?** There is nothing to type:
[install it as an extension](docs/connect.md), with pictures.
## What it does
**Reading**
| Tool | Answers |
|---|---|
| `whoami` | who the token belongs to, which Moodle, and what that token may do |
| `list_functions` | every web-service function your token is allowed to call |
| `my_courses` | your courses, with the id every other tool needs |
| `students` | who is enrolled: role, email, city, last access |
| `course_contents` | sections and modules as the students see them |
| `assignments` | assignments with due date, maximum grade, and the brief as plain text |
| `submissions` | who submitted what, with file names and download URLs |
| `submission_status` | one student on one assignment: state, grade, feedback, extension |
| `missing` | enrolled students who have **not** submitted — the morning-after list |
| `gradebook` | grade items with marks and feedback |
| `announcements` | recent posts in the course news forum |
| `attendance_sessions` | the sessions of each attendance register: date, duration, whether taken |
| `attendance_report` | presences and absences per student, excused apart, with an optional absence limit |
**The week's timetable** — `timetable` reads every lesson of a campus for one
week (lessons are attendance sessions, which Moodle shows as calendar events),
with lecturers and number of students from the enrolments and the room from the
session description (`Aula: DREAM`). It reports room clashes, a lecturer in two
places and lessons with no room, writes the week as a WhatsApp message, and can
save it as a web page: a timeline per room, each lecturer's own view, free rooms.
**Ready-made requests** — in Claude Desktop they appear in the menu, so nobody
has to write a prompt: *Orario della settimana*, *Carica i voti dei professori*,
*Controllo presenze del venerdì*, *Registri presenze in ritardo*, *Studenti a rischio* (in Italian, for the academic office in Florence;
see [the guide](docs/guida-segreteria.md)).
**Across courses, for the academic office**
| Tool | Answers |
|---|---|
| `late_registers` | registers not taken within 24 hours of the lesson (the ESE rule), grouped by lecturer: overdue, taken late, still in time |
| `students_at_risk` | one row per student across every course: absences over the limit, assignments not handed in, failing marks; trouble in two courses or of two kinds ranks first |
**Lecturers' grade files** — for the academic office that enters marks sent in
by lecturers as spreadsheets (one file per module, a matriculation number and a
mark per student for each assessment).
| Tool | Does |
|---|---|
| `grades_check` | reads a file or a whole folder: which course each file points at (from its name), which gradebook item each column goes to, and which rows are ready or blocked — mark not a number, student not enrolled in that course, same student twice |
| `grades_csv` | writes one CSV per file for Moodle's gradebook import (Grades ▸ Import ▸ CSV), mark and feedback side by side, plus `_to_check.csv` with every blocked row and why |
| `grades_verify` | after the import, reads the gradebook back and compares it with the lecturers' files, row by row |
Marks are copied, never computed: `57,5` and `57.5` are both 57.5, and anything
uncertain (`ABS`, `5 7`, a cell Excel turned into a date) is reported, not guessed.
Why a CSV and not a direct write: gradebook items created by hand (the usual
"Final" and "resit") have no web service that writes them; the CSV import is
Moodle's own way in.
**Writing** — these change what students see, so the server's instructions tell
the assistant to confirm with you before calling them.
| Tool | Does |
|---|---|
| `grade_submission` | mark and written feedback on one submission |
| `announce` | a post in the news forum; everyone enrolled is emailed |
| `mark_attendance` | one student's status in one session, e.g. absent → excused after a certificate |
The attendance tools need the `mod_attendance_*` functions to be part of your
token's web service. On many sites they are not: `whoami` says so
(`can_read_attendance`), and the site administrator can add them.
## Lecturer or academic office
The office's tools (grade files, late registers, students at risk and their
ready-made requests) are on by default. A lecturer who only needs their own
courses unticks **Staff tools** in the extension's settings (or sets
`MOODLE_STAFF_TOOLS=false`) and sees 17 tools instead of 22.
## What it deliberately does not do
**Upload course materials.** Moodle core has no web service that creates a
module or a resource, so no MCP server can add a file to a course section. Put
the materials where you already keep them (a course website, a repository) and
link to them from Moodle.
## Install
**As a Claude Desktop extension** — download `mcp-moodle-staff.mcpb` from the
[latest release](https://github.com/NiccoloSalvini/mcp-moodle-staff/releases/latest),
then Settings ▸ Extensions ▸ Install Extension… and fill in the two boxes.
Nothing else to install: Claude Desktop runs it. The
[illustrated walkthrough](docs/connect.md) covers this in full.
**From the command line**, for Codex, Claude Code or any other MCP client — it
builds itself on install, so there is nothing to clone:
```bash
npx -y github:NiccoloSalvini/mcp-moodle-staff
```
**From a clone:**
```bash
npm install && npm run build # dist/index.js
npm run bundle # dist/mcp-moodle-staff.mcpb
```
## Get a token
Your Moodle must have web services enabled. If *Preferences → Security keys*
exists for your account, copy the token for **Moodle mobile web service**. If
that page is empty — your role may lack `moodle/webservice:createtoken` — the
included script asks Moodle directly:
```bash
MOODLE_SITE=https://moodle.example.edu bash scripts/get-moodle-token.sh
```
It reads the password with `read -s`, never echoes it, never stores it and never
puts it on a command line. It writes `.env` with mode 600.
Use the exact base URL Moodle knows itself by. If you get
`requirecorrectaccess`, you have the wrong host — try it with and without `www`.
## Configure your MCP client
```json
{
"mcpServers": {
"moodle": {
"command": "npx",
"args": ["-y", "github:NiccoloSalvini/mcp-moodle-staff"],
"env": {
"MOODLE_URL": "https://moodle.example.edu/webservice/rest/server.php",
"MOODLE_TOKEN": "${MOODLE_TOKEN}"
}
}
}
}
```
For Codex, one line does it:
```bash
codex mcp add moodle --env MOODLE_URL=… --env MOODLE_TOKEN=… -- npx -y github:NiccoloSalvini/mcp-moodle-staff
```
Export `MOODLE_TOKEN` in the shell that launches the client (`set -a; . .env; set +a`)
rather than writing it into the JSON, so the credential stays out of version control.
Then ask `whoami` first. It reports how many functions your token can reach and
whether grading and posting are among them; most failures are a permission the
site has not granted, not a bug.
## Handling the token
A Moodle web-service token is a bearer credential carrying all of your rights,
including marking. Treat it as a password:
- It is sent in the **POST body**, never in the query string, because URLs are
written to proxy logs, to the server's access log and to any client's request log.
- Transport and Moodle errors are redacted before they become messages.
- Installed as an extension, the token goes in a field marked sensitive: the app
stores it, and it never appears in a configuration file you might share.
- `.env` is gitignored and written with mode 600.
- If a token is exposed, revoke it in *Preferences → Security keys* (or ask your
admin to delete it) and fetch a new one. Requesting a token again returns the
**same** one until the old is deleted.
## Personal data
These tools return real names, email addresses and submitted work. Anything an
assistant sees can end up in a transcript. Ask for the aggregate — how many are
missing, which one is at risk — before you ask for the list.
## Licence
MIT.
TDQS
Scored across 22 tools
Each tool targets a distinct resource or action: attendance sessions, attendance reports, late registers, and marking are clearly separated; the grade-import tools (check/csv/verify) are also distinct. Even the most similar tools, submissions and submission_status, are disambiguated by their descriptions.
All names use lowercase snake_case and are readable, but the convention is mixed: many read tools are bare nouns (students, assignments, announcements), while others use verb_noun (mark_attendance, list_functions) or noun_verb (grades_check, grades_verify). There is no consistent verb-first pattern across the set.
At 22 tools, the set sits in the 'borderline heavy' range. Each tool appears purposeful and none feel redundant, but the count is high enough that an agent will need to navigate a large surface for what is mostly attendance, grading, and reporting workflows.
The core teacher workflows are well covered: attendance tracking and marking, assignment submissions and grading, grade-file import verification, announcements, and student risk summaries. Minor gaps exist—for example, no bulk attendance-taking tool or assignment creation/update—but they do not break the main intended workflows.