Read the quizzes behind H5P elements

A quiz in Schulcloud is an H5P element, and a board hands over nothing but a
contentId — so a teacher's exercise was until now a line saying one exists.
The player shows a single question at a time, which makes it look like
something to step through or scrape. It is not:
`GET /api/v3/h5p-editor/params/{contentId}` returns the JSON the player is fed,
so one request holds every question, every option and which of them are
correct. (`play/{id}` is the same content plus the player's script lists: 74 kB
against 51 kB for the live quiz. Neither docs-json describes the service.)

get_h5p prints the exercise, and solutions=false keeps the options while
dropping the answers, so it can be used to ask the questions instead of
answering them. get_board names the exercise — title, question count, kinds —
rather than printing a bare id, and the crawl indexes its text, so a phrase
that exists only inside a quiz is now findable. That is the treatment pads
already get, for the same reason: it is course material and nothing else
surfaces it.

What varies is the shape inside `params`, which belongs to whichever H5P
library the teacher used. Modelled: MultiChoice, whose `behaviour.singleAnswer`
is the only honest source for "tick exactly one"; TrueFalse, whose `correct` is
the string "true"; the cloze libraries, which mark solutions inline as
`*answer:tip*`; SingleChoiceSet and Summary, which put the correct option first
and let the player shuffle; and Column. Anything else has its text harvested
and labelled unmodelled — an exercise reported as "0 questions" would be worse
than a clumsy rendering of one. The harvest skips the UI and l10n subtrees, or
a quiz reads as "Überprüfen, Wiederholen, Absenden".

Verified against this account's quiz, an H5P.QuestionSet of 20 MultiChoice
questions on a room's board: 239 tests, smoke 91/91 live-only and 93/93 with
the index.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-09-17 21:39:19 +02:00
parent 19d9bce2f7
commit a0cef532c6
10 changed files with 737 additions and 3 deletions

View File

@@ -7,6 +7,7 @@ import { registerResources } from './resources.ts';
import { registerContentTools } from './tools/content.ts';
import { registerFileTools } from './tools/files.ts';
import { registerFilesystemTools } from './tools/filesystem.ts';
import { registerH5pTools } from './tools/h5p.ts';
import { registerOverviewTools } from './tools/overview.ts';
import { registerRawTool } from './tools/raw.ts';
import { registerIndexTools } from './tools/index-tools.ts';
@@ -28,6 +29,9 @@ How the content is organised, and the usual path through it:
text block, link and attached file in one call.
- **Topics / lessons** ("Themen") — the older format. get_lesson.
- **Tasks** ("Aufgaben") — homework. list_tasks across all courses, get_task for one.
- **Quizzes and interactive exercises** are H5P elements on a board ("Quiz", "Test", "Übung"). get_board names
one and says how many questions it holds; get_h5p returns all of them with the correct options marked — the
player steps through one at a time, this does not. search finds their question text too.
- **Files** hang off boards, lessons and tasks. Every listing shows file ids; download_file fetches one and
extracts its text (PDF, Word, Excel, PowerPoint, OpenDocument) or returns an image inline.
- **The file manager ("Dateien")** is a separate store with a real folder tree, browsed with the fs_* tools:
@@ -72,6 +76,7 @@ export function createServer(config: Config, services?: Services): { server: Mcp
registerRoomTools(server, context);
registerFileTools(server, context);
registerFilesystemTools(server, context);
registerH5pTools(server, context);
registerSearchTool(server, context);
registerSubmissionTools(server, context);
registerIndexTools(server, context);