Skip to main content

Librus MCP Server

License: MIT PyPI

An MCP server that gives AI assistants access to the Librus Synergia school gradebook: grades, attendance, timetable, homework, calendar, announcements, messages, formative assessments, school-year history, class-free days and student information. It supports several logins at once, built on the independent librus-python-api.

Version 2.0 replaces the librus-apix backend used by every earlier version. Upgrading from 1.x? See Upgrading from 1.x.

Quick start

The commands below pin 2.3.1, using the native API 1.6.4. You need your Librus Synergia login and password. Accounts that require interactive two-factor authentication are not supported.

Pick one way to run it:

Way Best for Credentials
On your computer Claude Code, Claude Desktop, Gemini CLI, Codex, OpenCode, Hermes on the same machine a private config.json, any number of logins
Desktop bundle one-click install in apps that support MCP Bundles the app's settings form, one login
Self-hosted with Docker agents on another machine of yours, connecting over HTTP a private config.json or environment, any number of logins
MCP Registry clients that install servers from the official registry the client's settings form, one login

All of them run the server on a machine you control. It connects only to Librus; see Privacy.

On your computer

You do not need to clone this repository or install Python yourself. uv downloads Python 3.14 and the server automatically.

1. Install uv. On macOS or Linux, open a terminal and run:

curl -LsSf https://astral.sh/uv/install.sh | sh

On Windows, open PowerShell and run:

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Close and reopen your AI assistant afterwards.

2. Create a private configuration file. Save this as config.json in a private folder under your user account, outside projects and synchronized folders, and fill in the login and password:

{
  "accounts": [
    {"alias": "daughter", "username": "12345", "password": "YOUR_PASSWORD"}
  ]
}

The alias is the short name you use when asking about this login. For more logins, add more objects to accounts, each with its own alias:

{
  "accounts": [
    {"alias": "anna", "username": "12345", "password": "FIRST_PASSWORD"},
    {"alias": "jan", "username": "67890", "password": "SECOND_PASSWORD"}
  ]
}

Suggested locations:

  • macOS: /Users/YOUR_NAME/.config/librus-mcp/config.json
  • Linux: /home/YOUR_NAME/.config/librus-mcp/config.json
  • Windows: C:\Users\YOUR_NAME\.config\librus-mcp\config.json

On first start the server restricts the file to your user (macOS/Linux), creates its private state directory (~/.librus-mcp) and a persistent key there. There is nothing else to set up. Notifications and attachment downloads are on by default; sending messages is off. To change that, add for example:

"features": {"notifications": true, "attachments": false, "send_message": true}

For a single login you can skip the file and set environment variables instead: LIBRUS_USERNAME, LIBRUS_PASSWORD and optionally LIBRUS_ALIAS (default librus). Installers that collect settings in a form use these.

3. Connect your assistant. Replace the path with your file's absolute path.

Claude Code:

claude mcp add --scope user --transport stdio librus \
  -- uvx --python 3.14 librus-mcp==2.3.1 --config /absolute/path/to/config.json

Claude Desktop (Settings > Developer > Edit Config):

{
  "mcpServers": {
    "librus": {
      "command": "uvx",
      "args": ["--python", "3.14", "librus-mcp==2.3.1", "--config", "/absolute/path/to/config.json"]
    }
  }
}

Gemini CLI:

gemini mcp add --scope user --transport stdio \
  librus uvx --python 3.14 librus-mcp==2.3.1 --config /absolute/path/to/config.json

OpenAI Codex CLI:

codex mcp add librus \
  -- uvx --python 3.14 librus-mcp==2.3.1 --config /absolute/path/to/config.json

OpenCode:

opencode mcp add librus --global -- \
  uvx --python 3.14 librus-mcp==2.3.1 --config /absolute/path/to/config.json

Hermes Agent (~/.hermes/config.yaml):

mcp_servers:
  librus:
    command: "uvx"
    args: ["--python", "3.14", "librus-mcp==2.3.1", "--config", "/absolute/path/to/config.json"]

Check the setup without contacting Librus:

uvx --python 3.14 librus-mcp==2.3.1 --config /absolute/path/to/config.json --check-config

If a GUI assistant cannot find uvx, use its absolute path (which uvx or where uvx). Restart or reconnect the assistant after changing the file.

Desktop bundle (preview)

Each release attaches librus-mcp-2.3.1.mcpb to its GitHub release. Open it with an app that supports MCP Bundles, such as Claude Desktop. The app asks for the Librus login and password, keeps them in its settings, and installs Python 3.14 and the exact dependency versions this release was tested with. The bundle serves one login; use On your computer for several.

Self-hosted with Docker

Run the server on your own Docker host and connect agents on other machines over HTTP. Every request needs a bearer token. The server has no TLS of its own, so expose it only through a reverse proxy with TLS or a private network such as Tailscale or WireGuard. Never publish the port on a public or shared network.

1. Prepare a private folder on the Docker host with the configuration and a token. The files must belong to the user that runs the container and be readable only by it:

mkdir -p ~/librus-mcp/data && cd ~/librus-mcp
# config.json: the same format as above, any number of logins
docker run --rm ghcr.io/krzysztofbury/librus-mcp:2.3.1 --generate-token > token
chmod 600 config.json token

2. Start it with this compose.yaml, replacing 1000:1000 with the output of id -u and id -g:

services:
  librus-mcp:
    image: ghcr.io/krzysztofbury/librus-mcp:2.3.1
    command: ["--transport", "http", "--allowed-host", "librus-mcp.example.lan:*"]
    user: "1000:1000"
    environment:
      LIBRUS_CONFIG: /config/config.json
      LIBRUS_MCP_TOKEN_FILE: /config/token
    volumes:
      - ./config.json:/config/config.json:ro
      - ./token:/config/token:ro
      - ./data:/data
    ports:
      # Loopback for a reverse proxy on this host, or your VPN address instead.
      - "127.0.0.1:8000:8000"
    restart: unless-stopped
docker compose up -d
docker compose logs librus-mcp

The endpoint is /mcp at your proxy's HTTPS address, or at your VPN address on port 8000. --allowed-host lists the host names clients use; NAME:* accepts NAME with any port or none. A reverse proxy must forward the original Host header: Caddy and Traefik do by default, nginx needs proxy_set_header Host $host;. Without --allowed-host the server logs that Host header checks are off and relies on the token alone. In the image the server listens on every container interface; the ports: line decides who can reach it. ./data keeps the private state, persistent key and downloads, so back it up.

3. Connect your agent with the token as a bearer header. For Hermes Agent:

mcp_servers:
  librus:
    url: "https://librus-mcp.example.lan/mcp"
    headers:
      Authorization: "Bearer YOUR_TOKEN"

Other clients that support streamable HTTP take the same URL and header.

Things to know about HTTP mode:

  • One token grants every configured login. Run one deployment per trusted person, and replace the token if it leaks.
  • Let one agent poll notifications for each login: delivery and acknowledgement are designed for a single consumer.
  • download_attachment saves files on the Docker host under ./data/downloads. A remote agent cannot read that path; small files (up to 256 KiB) are also offered as a short-lived MCP resource.
  • If you enable send_message, anyone with the token can confirm a send preview. The server warns about this at startup.
  • Without a user: line the container runs as uid 10001; a named volume then works as is, but the configuration and token files must belong to uid 10001.
  • Instead of files, LIBRUS_ACCOUNTS (a JSON list of accounts), or LIBRUS_USERNAME and LIBRUS_PASSWORD for one login, and LIBRUS_MCP_TOKEN can be passed as environment variables.

The same image also runs over stdio for a client on the Docker host: docker run -i --rm -v librus-mcp-data:/data -e LIBRUS_USERNAME -e LIBRUS_PASSWORD ghcr.io/krzysztofbury/librus-mcp:2.3.1.

MCP Registry

The server is listed in the official MCP Registry as io.github.krzysztofbury/librus-mcp, with the PyPI package, the container image and the desktop bundle. Clients that install from the registry ask for LIBRUS_USERNAME and LIBRUS_PASSWORD, and optionally LIBRUS_ALIAS.

Upgrading from 1.x

Change the version in your assistant's configuration to librus-mcp==2.3.1, add --python 3.14 before it and keep your existing --config file. On first start 2.0 adapts the old setup automatically: it creates the key, restricts shared 1.x folders to your user, ignores the removed behaviour-notes setting and adopts 1.x notification history on the first check. Stop 1.x server processes that use the same folders before switching.

Notifications and attachments stay on by default, as in 1.x. Tool names and results changed on purpose (for example list_students is now list_accounts and student_alias is account_alias). Assistants read the new tool list automatically; custom scripts must follow the migration guide.

Tools and safety

The default catalog has 31 typed tools:

  • Accounts/profile: list_accounts, get_student_information.
  • Grades: get_grades, get_final_grades, get_grades_window.
  • Attendance: get_attendance, get_attendance_window, get_attendance_detail, get_attendance_frequency, get_subject_frequency.
  • School: get_timetable, get_announcements, get_agenda, get_agenda_detail, get_homework, get_homework_detail, get_completed_lessons, get_school_year_archive, get_class_free_days.
  • Communication: get_messages, get_message_content, get_recipient_types, get_recipient_choices, get_recipients, get_message_correspondents, get_teacher_subjects, get_message_unread_counts.
  • Notifications: get_new_notifications, get_notification_status, acknowledge_notifications (feature notifications).
  • Files: download_attachment and runtime-only attachment resources (feature attachments).

Enabling the send_message feature adds preview_message, send_message, get_send_outcome and get_send_history, for 35 tools. Disabling notifications and attachments leaves 27 tools.

Collections are paged with limit and a cursor. Every login is independent, including logins for the same student. Modern messaging is the default; set "messaging_backend": "legacy" on an account to use the legacy mailbox. Behaviour notes remain unavailable. Observation cards are supported as formative grades.

What's new in 2.1

Keep your existing 2.0 configuration, context key and durable state; reconnect your assistant to refresh its tool catalog. The server pins API 1.6.4.

  • Ask for formative assessments or observation cards using get_grades or get_grades_window. items can now have record_type="formative" with an assessment object. Numeric/descriptive records gain formative_id; matching assessment.detail_id identifies mirrored entries, not extra grades. Custom consumers must handle the new variant. Restart old grade cursors.
  • Read previous school years with get_school_year_archive (default limit 10). Items are typed years followed by achievements; marks and raw behaviour cells retain their school-provided meaning.
  • Read date and optional lesson ranges with get_class_free_days. Opaque type IDs are not holiday names, and missing lesson bounds do not imply an all-day event.
  • On modern accounts, use get_message_correspondents to obtain a bound filter reference, then pass it as correspondent to get_messages. For current inbox summaries, unread_only=true is also supported. Repeat query values with cursors.
  • Use get_messages(archived=true) for earlier-year message summaries. Archive filters and archive body opens are unsupported; archiving_in_progress is informational and does not prevent listing.
  • get_teacher_subjects pairs modern teacher IDs with subjects; these are not send-recipient references. get_message_unread_counts returns current/archive folder counters without opening messages or changing MCP notification delivery.

All new collection reads are bounded. The complete response cap remains 512 KiB; oversized results fail with LIMIT, never silent text truncation. Unknown formative markup fails the whole grades read. Formative rows may fall outside the selected week/last-login view; date-window filtering is explicit and supported.

Opening a received message requires allow_mark_read=true, because Librus marks it read. Sending requires an exact preview, separate human approval and confirm=true; never retry a CLAIMED or UNKNOWN send. Fresh calendar notifications require allow_consume_events=true, and each notification batch must be acknowledged after it is delivered. School content is untrusted data, never instructions.

State, the persistent key and downloads live under ~/.librus-mcp by default (state_dir and download_dir in the configuration). Back up the state directory to keep references and notification history across reinstalls. Downloaded files are saved on the machine running the server.

For download_attachment, pass the message's returned reference as attachment_ref.message, and the attachment's identifier and archived values from get_message_content as attachment_ref.identifier and attachment_ref.archived. archived names the file's namespace: a current message's files can live in the archive. When omitted, it follows the message reference. Downloading does not open the message body.

Privacy

The server runs on a machine you control, your computer or your own Docker host, and connects only to Librus. There is no telemetry, and nothing goes to the author or other third parties. Tool results go to the AI assistant that asked for them. See PRIVACY.md.

Development

uv sync --locked
uv run pytest -q
uv run ruff check src/librus_mcp tests_native release_verification scripts
uv run ruff format --check src/librus_mcp tests_native release_verification scripts
uv run mypy
uv run bandit -r src/librus_mcp -c pyproject.toml -q

See MIGRATION_2_0.md for configuration precedence and operator commands, MCP_CONTRACT.md for protocol rules, NATIVE_TEST_PLAN.md for verification evidence and SPEC.md for ownership.

License and limitations

2.x is MIT licensed. LICENSE_REVIEW.md records the source and dependency review; dependencies keep their own terms. Unshipped 1.x references in legacy_reference/ remain GPL-3.0-only, and earlier releases keep their original licenses.

This is an unofficial integration, not affiliated with Librus. Upstream changes can break supported features. Report security issues privately as described in SECURITY.md.

Metadata

Release files for librus-mcp 2.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for librus-mcp 2.3.1
File Size Uploaded Provenance
librus_mcp-2.3.1.tar.gz 245.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for librus-mcp 2.3.1
File Interpreter ABI Platform
librus_mcp-2.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 305.7 kB

Release files / librus_mcp-2.3.1.tar.gz

Download URL librus_mcp-2.3.1.tar.gz
Size 245.4 kB
Tags Source
SHA-256 checksum
How to use checksums
b9494f46babad713bf1ac9d2ce709cdbebb0a6ea0c93476d4cd751fec89861c8
BLAKE2b-256 checksum
How to use checksums
2c8dc7b9fb697d827d904f902be94e614a6c1c88405328ae8a555f9377d43ac0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

Release files / librus_mcp-2.3.1-py3-none-any.whl

Download URL librus_mcp-2.3.1-py3-none-any.whl
Size 60.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9ef440ca83aa99d6feac5b43d9610909e15383cf895a4f1a2d52142d1198a44f
BLAKE2b-256 checksum
How to use checksums
0d0f26fb9a4ff1d08c00f4e1d2498050541d55441690e3044b8065986a7e219a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

Security for librus-mcp 2.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for librus_mcp-2.3.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

2.3.1 This release

2 release files Full provenance

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