An MCP server that builds Ліцейний Простір (Pidbirtsi Lyceum LMS) courses end-to-end — course → chapters → lessons → quiz → questions → co-authors → certificate — by calling the LMS authoring API. Point an MCP client (Claude Desktop, Claude Code, …) at it and say:
"Build me a course about percentages with 3 lessons and a 5-question quiz."
…and it assembles everything via tool calls instead of clicking through the UI.
Live platform: https://pidbirtsi.school
MCP client ⇄ this server (FastMCP) ⇄ HTTPS ⇄ /api/authoring/ (Bearer token) ⇄ Django LMS
Every course is authored as the instructor the token belongs to. The API enforces the same ownership rules as the human UI, is rate-limited per token, and sanitises all HTML server-side.
You need an AuthoringToken bound to your instructor account. The easiest way: open the LMS, go to your instructor cabinet → AI-доступ, and copy the ready-made config block (it already contains the right host and a fresh token).
Admins can also create one in Django admin → Токени авторингу → Add →
pick the instructor → save → copy the key.
Treat the token like a password — it grants course-authoring as that instructor. Rotate it any time by re-issuing on the AI-доступ page (the old one stops working immediately).
Shared / service token? Courses are owned by the token's instructor. If your token belongs to a shared account, the human teacher would otherwise hit 403 in the browser. Either pass
owner_emailtocreate_course/create_course_bundle, or calladd_instructor(course_slug, email)after creating — both make the person a real instructor of the course.
Requires Python 3.10+.
If you have uv:
uvx --from git+https://github.com/Yelliko/pidbirtsi-lms-mcp pidbirtsi-lms-mcppipx install git+https://github.com/Yelliko/pidbirtsi-lms-mcp
# or:
pip install git+https://github.com/Yelliko/pidbirtsi-lms-mcpgit clone https://github.com/Yelliko/pidbirtsi-lms-mcp
cd pidbirtsi-lms-mcp
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python -m pidbirtsi_lms_mcp.server| Var | Example | Meaning |
|---|---|---|
LMS_API_BASE |
https://pidbirtsi.school |
LMS base URL (use http://127.0.0.1:8000 for local dev) |
LMS_API_TOKEN |
xY3… |
the AuthoringToken key from step 1 |
Claude Code (claude mcp add or an .mcp.json entry):
{
"mcpServers": {
"lms-authoring": {
"command": "uvx",
"args": ["--from", "git+https://github.com/Yelliko/pidbirtsi-lms-mcp", "pidbirtsi-lms-mcp"],
"env": {
"LMS_API_BASE": "https://pidbirtsi.school",
"LMS_API_TOKEN": "PASTE_TOKEN_HERE"
}
}
}
}If you cloned instead of using uvx, use the script path:
{
"mcpServers": {
"lms-authoring": {
"command": "python",
"args": ["/abs/path/to/pidbirtsi-lms-mcp/src/pidbirtsi_lms_mcp/server.py"],
"env": {
"LMS_API_BASE": "https://pidbirtsi.school",
"LMS_API_TOKEN": "PASTE_TOKEN_HERE"
}
}
}
}Claude Desktop (claude_desktop_config.json): same mcpServers block.
| Tool | Purpose |
|---|---|
list_courses |
see existing courses + grab a slug |
create_course |
empty DRAFT course → returns slug |
add_chapter |
add a розділ → returns chapter_id |
add_lesson |
add an урок (HTML text + optional YouTube URL) |
set_quiz |
create/update the course quiz settings |
add_question |
add a question (single/multiple); supports an image_url on the question and on any answer |
apply_certificate |
attach a ready blank (classic/ornament/minimal) + seed fields |
add_contributor |
add a co-author ("Автор курсу") shown on the public page |
add_instructor |
give a real person (by email) instructor access so they can edit the course in the browser |
set_access_mode |
public / invite / whitelist enrolment |
submit_for_review |
move course → REVIEW |
publish_course |
publish (staff token only) |
create_course_bundle |
build the whole course from one spec, atomically |
create_course_bundle is the workhorse — pass a full spec and it builds
everything in a single transaction (rolls back entirely on any error). See
examples/course_spec.json for a complete spec,
and the tool's docstring for every field.
Questions and individual answers can carry a picture. Pass image_url (a
public http(s) link) on the question and/or on any answer dict — the server
downloads it, verifies it's a real image (PNG/JPG/WebP/GIF, ≤5 MB) and rejects
private/loopback hosts. An answer (or a question) may be image-only with no
text. Example:
{"text": "Який це прапор?", "type": "single",
"answers": [
{"image_url": "https://example.com/ua.png", "correct": true},
{"image_url": "https://example.com/pl.png"}
]}| Mode | Behaviour |
|---|---|
public |
listed in the catalog, anyone can self-enrol |
invite |
hidden; join only via an invite link |
whitelist |
hidden; self-enrol only if the email is on the course list |
- The token is a bearer credential — store it in the MCP client config, never commit it. Rotate by re-issuing on the AI-доступ page (or deactivating the row in admin).
- The API is rate-limited per token and CSRF-exempt (token auth, not cookies).
- HTML fields (descriptions, lesson text) are sanitised server-side (script/iframe stripped).