import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { Config } from '../config.ts'; import { ServerContext } from '../context.ts'; import type { Services } from '../services.ts'; import { registerPrompts } from './prompts.ts'; 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'; import { registerNoteTools } from './tools/notes.ts'; import { registerSearchTool } from './tools/search.ts'; import { registerRoomTools } from './tools/rooms.ts'; import { registerSubmissionTools } from './tools/submissions.ts'; import { registerUntisTools } from './tools/untis.ts'; export const SERVER_NAME = 'schulcloud-mcp'; export const SERVER_VERSION = '0.1.0'; const INSTRUCTIONS = `Read-only access to a Schulcloud (HPI Schul-Cloud / Schulcloud-Verbund-Software) account. How the content is organised, and the usual path through it: - **Courses** ("Kurse") are the top level — list_courses, or get_dashboard for the ones the user has pinned. - A course page (get_course) holds three kinds of thing: - **Column boards** — where most current teaching material lives. get_board returns every column, card, 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: /my (Persönliche Dateien), /courses/ (Kurs-Dateien), /teams/ (Team-Dateien) and /shared (Geteilte Dateien). **Many teachers put their material only here**, so when a course page looks empty or the worksheets are not on its boards, look in /courses/. fs_list and fs_tree browse, fs_find finds by name, fs_read opens a file. list_files and download_file do not see these files. - **Submissions** ("Abgaben") — what the user handed in. get_task shows that task's submission: the files, the graded flag, the grade, what the user wrote, and the teacher's written feedback. A grade is a percentage (0-100) or absent — there is no textual grade — and teachers often grade with the written feedback alone, so "graded by feedback" is a complete result, not missing data. list_submissions surveys them across tasks ("what is still ungraded?"). The written parts are read from the web page because no API exposes them, so they can be missing even when feedback exists — if none is shown for a graded submission, say it was not found rather than that none was given. On a teacher account these tools report other people's submissions too. **The user's own notes are a third source, and often the best one.** When the note tools are listed, the user keeps notes from their lessons as Markdown files: list_notes and get_note read them, search finds them by content, and add_note writes one. They record what a teacher said and stressed, which no upload does — so consult them whenever the question is what was covered in class, what a topic means "the way we did it", or what to revise for a test, and say when a note disagrees with the material. Notes are the user's own words: quote them, do not silently correct them. **The timetable is not in Schulcloud.** When the untis_* tools are listed, the school's schedule lives in WebUntis and they are the only way to it: untis_timetable says which lessons a day actually holds, what was cancelled ("Entfall"), what is a substitution ("Vertretung") and what a teacher noted on a period — announced tests are usually in those notes. Schulcloud holds the material for those lessons, so the two go together: take the subject from untis_timetable, then find its course with list_courses. untis_homework is the class register's homework, which is a different list from Schulcloud's tasks; check both. untis_lesson_topics says what previous lessons of a subject actually covered — pass it a subject to read back over a whole term, which is the fastest way to reconstruct what a course has done. Those class-register entries are in the index too, so search finds them beside the Schulcloud material. When the user names a topic rather than a course, use search — the API has no search endpoint, so it walks the courses and matches client-side, which takes a few seconds but covers board text and file names. The user can also attach a course or room directly (resources schulcloud://courses/ and schulcloud://rooms/). An attached one is exactly what get_course or get_room returns, so do not fetch it again — continue from its ids. Everything that touches Schulcloud and WebUntis is read-only: no tool here can change the school account, hand anything in, or mark anything done. The one exception writes nowhere near them — add_note, when it is listed, saves a file in the user's own notes directory.`; export function createServer(config: Config, services?: Services): { server: McpServer; context: ServerContext } { const context = new ServerContext(config, services); const server = new McpServer( { name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {}, logging: {} }, instructions: INSTRUCTIONS }, ); registerOverviewTools(server, context); registerContentTools(server, context); registerRoomTools(server, context); registerFileTools(server, context); registerFilesystemTools(server, context); registerH5pTools(server, context); registerSearchTool(server, context); registerSubmissionTools(server, context); registerIndexTools(server, context); // Only when a key is configured: the tools are not offered at all otherwise. registerUntisTools(server, context); // Same rule, for NOTES_DIR. registerNoteTools(server, context); registerRawTool(server, context); registerResources(server, context); registerPrompts(server, context); return { server, context }; }