Skip to main content

EduPage MCP Server

GitHub release Quality gates Security Container security Coverage drift

Project

A Model Context Protocol (MCP) server that exposes the full functionality of the edupage-api Python library to AI agents such as opencode, Claude, Cursor and any other MCP client.

EduPage is a school information system used across Europe. This server lets you query and operate a student / teacher / parent EduPage account directly from your agent: timetables, grades, homework, substitutions, meals (including ordering), messages, rosters, parent child-switching and more — including multiple schools (e.g. two children attending different schools).

⚠️ Unofficial API. Like all EduPage MCP servers, this relies on the community-maintained edupage-api library, which talks to EduPage's undocumented endpoints. Use read-only features freely; use the write features (send_message, meal ordering, child switching) carefully.


Table of Contents


Why another EduPage MCP server?

Two other EduPage MCP servers already exist:

Both are good and I have no affiliation with them — they are simply referenced here for honest comparison. They primarily focus on the read-only surface of the API.

This project deliberately goes further:

Capability mhlavac mrtineu (PyPI) this project
Timetables (own + any teacher/class/room)
Grades (all / by term & year)
Substitutions / timetable changes
Meals — read menu
Meals — choose / sign-off / rate
Send messages (send_message)
Parent student switching (switch to/from student) partial (list)
2FA login flow (device + email code)
Login via session id (PHPSESSID)
Portal login (login_auto)
Next ringing time / bell schedule
Raw session custom request
Multiple schools (auto-login + discovery)
Role-aware (parent / student / teacher)

Key differentiators:

  • Multi-school automatic discovery. Set EDUPAGE_SUBDOMAINS with one shared login and the server auto-discovers students across all schools — no need to maintain a manual "Viktor → school A, Tamara → school B" mapping. A student at two schools (e.g. Tamara at iprskola + cvcmalacky) is found automatically with separate per-school results.
  • Role-aware tools. The server detects whether you're a parent, student, or teacher at each school and behaves accordingly — get_student_timetable switches to the student account for parents, returns direct timetables for students. No tool duplication.
  • Full write surface. Meal ordering/rating, message sending, student switching — the other servers don't cover these.

What it provides

A single stdio MCP server exposing 46 tools (published on PyPI as edupage-mcp-full):

  • Authenticationlogin, login_auto, login_all, login_from_session, two_factor_check_confirmed, two_factor_finish, auth_status, user_id
  • Timetablesget_my_timetable, get_timetable (teacher/student/class/ classroom), get_student_timetable (student by name, cross-school), get_next_week_timetable, get_next_ringing_time, get_periods, school_year
  • Studentsfind_student (name → person_id, cross-school), get_student_timetable (cross-school, role-aware), scan_students (auto-discover all students across schools), get_my_students (classmates or school-wide for parents), switch_to_student (by id or name, parent only), switch_to_parent, clear_student_cache (force refresh cached student lists)
  • Schoolsget_schools (logged-in schools with role per school)
  • Gradesget_grades
  • Notifications / timelineget_notifications, get_notification_history, get_homework, get_assignments, get_absences, get_upcoming_events, get_news
  • Substitutionsget_timetable_changes, get_missing_teachers
  • Mealsget_meals, choose_meal, sign_off_meal, rate_meal
  • Day summariesget_day_summary (one call: timetable, substitutions, missing teachers, grades, meals, homework, assignments, absences, news, events, notifications for a date — "what happened yesterday at school" in a single round trip; each section is isolated so one failure doesn't kill the report). Bundles an OpenCode skill (school-day-summary) for turning it into a human-readable daily report; see Skills.
  • Rostersget_students, get_all_students, get_teachers, get_classes, get_classrooms, get_subjects, get_my_students
  • Actionssend_message, switch_to_student, switch_to_parent, custom_request

Getting started

You need an MCP-capable client (opencode, Claude Desktop, Cursor, etc.).

1. Install

If you are using an AI coding client, a simple prompt is often enough to get started, for example: "Install the EduPage MCP as described in this GitHub repository oliverhruby/edupage-mcp". Most MCP-capable clients can then guide you through the available setup options.

Option A — from MCP Registry (recommended, one-click in VS Code / GitHub Copilot)

The server is listed in the MCP Registry. In VS Code or GitHub Copilot, search for "EduPage MCP" and install with one click. Or use the direct deeplink: mcp://install/io.github.oliverhruby/edupage-mcp

Option B — from PyPI

Use this for normal usage with a released version.

Requirements: uv for uvx, or Python 3.10+ for pip.

uvx edupage-mcp-full
# or, if you prefer pip (into whatever environment your MCP client uses):
pip install edupage-mcp-full

uvx runs the package without a persistent install. If uvx is unavailable, install uv first (pip install uv or winget install astral-sh.uv).

Option C — from GitHub (latest source)

Use this if you want the latest changes before a PyPI release.

Requirements: uv for uvx, or Python 3.10+ for pip.

uvx --from "git+https://github.com/oliverhruby/edupage-mcp.git" edupage-mcp-full
# or
pip install "git+https://github.com/oliverhruby/edupage-mcp.git"

Option D — Docker

Use this for an isolated container runtime.

Requirements: Docker.

Pull a prebuilt image (recommended):

docker pull ghcr.io/oliverhruby/edupage-mcp:latest

docker run --rm -i \
  -e EDUPAGE_USERNAME=your_username \
  -e EDUPAGE_PASSWORD=your_password \
  ghcr.io/oliverhruby/edupage-mcp:latest

Version tags are also available (for example v0.4.0) if you prefer pinned images.

Build locally from source (fallback):

docker build -t edupage-mcp-full .

docker run --rm -i \
  -e EDUPAGE_USERNAME=your_username \
  -e EDUPAGE_PASSWORD=your_password \
  edupage-mcp-full

The container uses the same environment variables described in Configure credentials. It also includes a HEALTHCHECK (stdio process liveness by default; local TCP check in HTTP transport modes).

For HTTP transports, set optional runtime vars:

  • MCP_TRANSPORT: stdio (default), sse, or streamable-http
  • MCP_HOST: bind host (default 127.0.0.1)
  • MCP_PORT: bind port (default 8000)
  • MCP_API_KEY: optional bearer token for HTTP auth

When MCP_API_KEY is set, HTTP requests must include Authorization: Bearer <key>. If MCP_API_KEY is not set, HTTP endpoints are unauthenticated. For production, prefer proper authentication and TLS via a reverse proxy or API gateway.

pyproject.toml pins mcp<2 (the stable FastMCP v1 API). mcp 2.x renamed FastMCP to MCPServer and changed the API surface; this server targets the FastMCP v1 API for simplicity and stability.

Option C — development from source

Use this if you are contributing or debugging locally.

Requirements: Python 3.10+.

git clone https://github.com/oliverhruby/edupage-mcp.git
cd edupage-mcp
uv sync               # or: python -m venv .venv && .venv/bin/python -m pip install -e .
uv run edupage-mcp-full

Option D — Docker

Use this for an isolated container runtime.

Requirements: Docker.

Pull a prebuilt image (recommended):

docker pull ghcr.io/oliverhruby/edupage-mcp:latest

docker run --rm -i \
  -e EDUPAGE_USERNAME=your_username \
  -e EDUPAGE_PASSWORD=your_password \
  ghcr.io/oliverhruby/edupage-mcp:latest

Version tags are also available (for example v0.4.0) if you prefer pinned images.

Build locally from source (fallback):

docker build -t edupage-mcp-full .

docker run --rm -i \
  -e EDUPAGE_USERNAME=your_username \
  -e EDUPAGE_PASSWORD=your_password \
  edupage-mcp-full

The container uses the same environment variables described in Configure credentials. It also includes a HEALTHCHECK (stdio process liveness by default; local TCP check in HTTP transport modes).

For HTTP transports, set optional runtime vars:

  • MCP_TRANSPORT: stdio (default), sse, or streamable-http
  • MCP_HOST: bind host (default 127.0.0.1)
  • MCP_PORT: bind port (default 8000)
  • MCP_API_KEY: optional bearer token for HTTP auth

When MCP_API_KEY is set, HTTP requests must include Authorization: Bearer <key>. If MCP_API_KEY is not set, HTTP endpoints are unauthenticated. For production, prefer proper authentication and TLS via a reverse proxy or API gateway.

pyproject.toml pins mcp<2 (the stable FastMCP v1 API). mcp 2.x renamed FastMCP to MCPServer and changed the API surface; this server targets the FastMCP v1 API for simplicity and stability.

2. Configure credentials

Either set environment variables or pass credentials to login (see Prompt examples).

# Windows (persistent, per-user)
setx EDUPAGE_USERNAME "your_username"
setx EDUPAGE_PASSWORD "your_password"
setx EDUPAGE_SUBDOMAINS "s1,s2,s3"       # optional: multiple schools (auto-login + discovery)

# macOS / Linux
export EDUPAGE_USERNAME="your_username"
export EDUPAGE_PASSWORD="your_password"
export EDUPAGE_SUBDOMAINS="s1,s2,s3"     # optional

Single school? Just set EDUPAGE_USERNAME + EDUPAGE_PASSWORD. The server auto-discovers your school via the EduPage portal on startup — no subdomain needed.

Multiple schools? Add EDUPAGE_SUBDOMAINS (comma-separated). The server logs into all of them on startup with your shared credentials.

3. Register with your MCP client

opencode — add to ~/.config/opencode/opencode.json (or opencode.jsonc):

{
  "mcp": {
    "edupage": {
      "type": "local",
      "enabled": true,
      "command": ["uvx", "edupage-mcp-full"],
      "env": {
        "EDUPAGE_USERNAME": "{env:EDUPAGE_USERNAME}",
        "EDUPAGE_PASSWORD": "{env:EDUPAGE_PASSWORD}",
        "EDUPAGE_SUBDOMAINS": "{env:EDUPAGE_SUBDOMAINS}"
      }
    }
  }
}

Put credentials in your shell/environment (or a .env) and reference them with {env:VAR}, or hardcode them under env: directly. uvx will auto-provision the package the first time; it must be on your PATH.

Claude Desktop / Cursor — use claude_desktop_config.json / .mcp.json with a mcpServers entry in the standard shape, pointing command/args at the venv python and the edupage_mcp.py path, plus an env block with your credentials.

After editing client config, restart the client so the MCP server is loaded.


Prompt examples

User prompt Likely tool call(s) Expected response
"Are we connected and logged in?" auth_status Connected status, active school/subdomain, and login state per school.
"What classes do I have today?" get_my_timetable A short timetable summary for today.
"Show me the 9.A schedule for 2026-09-10" get_timetable target_type="class" target_id="9.A" date_str="2026-09-10" Class timetable for that date.
"What grades do I have this term?" get_grades term="FIRST" year=2026 Subject-by-subject grade overview for the selected term/year.
"Any substitutions today?" get_timetable_changes Changes, cancellations, and replacements for today.
"What is for lunch and order option 2 for tomorrow" get_mealschoose_meal date_str="2026-09-10" meal_type="lunch" number=2 Meal menu and order confirmation (or a clear error if unavailable).
"Find Viktor's timetable for tomorrow" get_student_timetable name="Viktor" date_str="2026-09-10" Viktor's timetable; if found in multiple schools, one result per school.
"List teachers and send a hello to Teacher456" get_teacherssend_message recipient_id="Teacher456" body="Hello!" Teacher list plus message sent confirmation.

Multiple schools & automatic student discovery

Each subdomain (school) keeps its own logged-in session. There are two ways to log in to several schools at once:

A) Automatic on startup (recommended). Set EDUPAGE_SUBDOMAINS (a comma-separated list) plus the shared EDUPAGE_USERNAME / EDUPAGE_PASSWORD — the server logs into all of them when it launches, so every tool is immediately ready and students are discoverable across all schools with no login call and no student→school mapping:

setx EDUPAGE_SUBDOMAINS "zssturovamalacky,iprskola,cvcmalacky"   # Windows
export EDUPAGE_SUBDOMAINS="zssturovamalacky,iprskola,cvcmalacky" # macOS / Linux
get_schools        # lists zssturovamalacky, iprskola, cvcmalacky (logged in, with role)
scan_students      # discovers Viktor and Tamara across those schools
get_student_timetable name="Tamara"   # is found at iprskola AND cvcmalacky

B) On demand with login_all. Authenticate several schools at once, then pass subdomain to any data tool (it defaults to the last active subdomain when omitted):

login_all subdomains="zssturovamalacky,iprskola" usernames="u1,u2" passwords="p1,p2"

get_my_timetable subdomain="zssturovamalacky"
get_my_timetable subdomain="iprskola"
auth_status          # shows all logged-in subdomains + which is active

You can also call login once per school to add/lookup sessions incrementally.

Single school? No EDUPAGE_SUBDOMAINS needed — the server auto-discovers your school via the portal on startup. For two or more schools, set EDUPAGE_SUBDOMAINS (auto-login) or use login_all / repeated login calls.


Students by name (e.g. "timetable for Viktor")

Because the server auto-discovers students across all logged-in schools, you don't need to know or state which school a student is in. Just ask for the timetable by name and the server searches every school it's logged into:

"timetable for Viktor"  ->  get_student_timetable name="Viktor"

get_student_timetable (with no subdomain):

  1. searches every logged-in school for a student whose first/last/full name matches (scan_students does just the discovery step),
  2. for each school where the student is found, switches to the student account if you're logged in as a parent, returns that student's timetable for the date, and switches back to the parent account afterwards,
  3. returns one result per school.

A student attending more than one school (e.g. Tamara at iprskola + cvcmalacky) therefore yields a list of two per-school timetables — separate results, never merged. This is the built-in replacement for maintaining a manual "Viktor → zsskola1" mapping: with EDUPAGE_SUBDOMAINS set, discovery is fully automatic.


Tool reference

Tool Description Writes?
login Log in with username/password/subdomain (env vars supported) ✅ session
login_auto Log in via the EduPage portal (auto-detect school) ✅ session
login_all Log in to multiple schools in one call ✅ session
login_from_session Create a session from an existing PHPSESSID cookie ✅ session
two_factor_check_confirmed Check if 2FA was approved on a device
two_factor_finish Finish 2FA (email/app code or device confirmation) ✅ session
auth_status Which subdomains are logged in + active one
user_id Logged-in user id
school_year Current school year
get_my_timetable Logged-in user's timetable for a date
get_timetable Timetable of a teacher/student/class/classroom
get_student_timetable Student's timetable by name or id (role-aware, cross-school) ✅ session
get_next_week_timetable Mon–Fri timetable for next week
get_next_ringing_time Next bell (break/lesson) at a given time
get_periods Bell schedule (period start/end times)
get_grades Grades, optionally by year & term
get_notifications Timeline notifications
get_notification_history Timeline notifications since a date
get_homework Homework from the timeline
get_assignments Homework/tests/exams from the timeline
get_absences Absence records from the timeline
get_upcoming_events Trips/excursions/meetings/holidays
get_news School news
get_timetable_changes Substitutions / timetable changes for a date
get_missing_teachers Teachers missing on a date
get_day_summary One-call daily report (timetable, substitutions, teachers, grades, meals, homework, assignments, absences, news, events, notifications) for a date; student by name/id (role-aware)
get_meals Meal menu (snack/lunch/afternoon snack; include_breakfast/include_dinner add extras)
choose_meal Order a meal
sign_off_meal Cancel an ordered meal
rate_meal Rate a meal (quality/quantity)
get_students Students in the logged-in user's class
get_all_students All students in the school (short list)
get_teachers All teachers
get_classes All classes
get_classrooms All classrooms
get_subjects All subjects
get_my_students Students visible to the logged-in account (one school)
find_student Look up a student's person_id by name (cross-school)
scan_students Auto-discover students across all logged-in schools
clear_student_cache Clear cached student rosters (one school or all schools) ✅ cache
get_schools List logged-in schools + role per school
send_message Send a message to a user
switch_to_student Switch to a student account by id or name (parent only) ✅ session
switch_to_parent Switch back to the parent account ✅ session
custom_request Raw request through the active session (GET/POST)

Data & safety notes

  • Most tools are read-only. The ones marked Writes? ✅ mutate EduPage state (sent messages, ordered meals, switched accounts). Use them with care.
  • get_homework, get_assignments, get_absences, get_upcoming_events and get_news derive their data from the timeline notifications — if the school doesn't push certain event types, those tools may return empty lists.
  • get_missing_teachers is marked experimental upstream (parses HTML from the substitution page) and can raise if a teacher's name no longer matches.
  • Meal rate_meal and ordering depend on the school publishing menus with the matching identifiers; not all schools expose ratings.
  • get_meals first tries the per-student meal-ordering endpoint (needed for ordering/ratings). When a school doesn't enable that, it falls back to the school's public canteen menu widget (/menu/?wid=menu_CanteenMenu_1), which is read-only (no ordering) and may include extra meals — pass include_breakfast=true / include_dinner=true to also get Raňajky/Večera.

Skills

The package ships an OpenCode skill (school-day-summary) with the wheel at <site-packages>/edupage_mcp/skills/school-day-summary/SKILL.md. It teaches an agent how to turn get_day_summary into a human-readable daily school report.

To register it with OpenCode, either:

  • copy it to OpenCode's global skills dir:
    mkdir -p ~/.config/opencode/skills/school-day-summary
    cp <site-packages>/edupage_mcp/skills/school-day-summary/SKILL.md \
       ~/.config/opencode/skills/school-day-summary/SKILL.md
    
  • or point an agent at the skill file in site-packages.

Restart OpenCode after installing so the skill is loaded; the agent can then answer prompts like "what happened at school yesterday for my kids?" by making a single get_day_summary call per child (falling back to individual tools if a section fails).


Contributing

Contributor and maintainer guidance is in CONTRIBUTING.md.

  • Contribution workflow and local setup
  • Architecture and implementation details
  • Release process (PyPI, GitHub Releases, GHCR)
  • CI quality gates and upstream coverage drift checks

Limitations

  • Unofficial/read-mostly by design. EduPage can change its endpoints at any time; reliability ultimately depends on edupage-api, not this wrapper.
  • No CAPTCHA bypass. If EduPage presents a CAPTCHA during login, log in via browser first, then use login_from_session with the resulting PHPSESSID.
  • 2FA requires human interaction (approve on device or provide a code).
  • Parent/teacher accounts are only partially verified upstream; some parent methods are best-effort.
  • The auth session lives for the lifetime of the MCP server process; restarting the client means logging in again.
  • Cross-school student discovery depends on being logged into all relevant schools (via EDUPAGE_SUBDOMAINS, login_all, or repeated login calls). If a school is not logged in, that student's results from that school cannot be discovered.

Support

If you like this project and want to support or request a feature, send me a beer, it keeps my mind relaxed and ideas will come :-)

Support via PayPal


License

MIT © Oliver Hrubý

This project is not affiliated with or endorsed by Ascora (EduPage) or by the authors of edupage-api. EduPage is a registered trademark of its respective owner(s).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

edupage_mcp_full-0.4.5.tar.gz (39.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

edupage_mcp_full-0.4.5-py3-none-any.whl (31.6 kB view details)

Uploaded Python 3

File details

Details for the file edupage_mcp_full-0.4.5.tar.gz.

File metadata

  • Download URL: edupage_mcp_full-0.4.5.tar.gz
  • Upload date:
  • Size: 39.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for edupage_mcp_full-0.4.5.tar.gz
Algorithm Hash digest
SHA256 a6317ec5cc0febc7cc8279d11d7a6e9d88d68d00930029fb3a4e3f0693f05df4
MD5 a48fdda71727524f4f65081c1094eae7
BLAKE2b-256 a465d8307600088c8b3b45e2af59147f8f2ed66865f7ff563b1f8d352d12679e

See more details on using hashes here.

Provenance

The following attestation bundles were made for edupage_mcp_full-0.4.5.tar.gz:

Publisher: publish.yml on oliverhruby/edupage-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file edupage_mcp_full-0.4.5-py3-none-any.whl.

File metadata

File hashes

Hashes for edupage_mcp_full-0.4.5-py3-none-any.whl
Algorithm Hash digest
SHA256 81d5caf56b40e048435108f047add1f1afdba5c3216c325e1ec9a393b76c2faf
MD5 fe7ca8f8f7c3a1b0f05ed9d4434a85a5
BLAKE2b-256 ee5176238c086cf7d6962550755c5c02ad1889a75661c955eb03cfc79e902378

See more details on using hashes here.

Provenance

The following attestation bundles were made for edupage_mcp_full-0.4.5-py3-none-any.whl:

Publisher: publish.yml on oliverhruby/edupage-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.6

2 files

This release

0.4.5 This release

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page