Skip to main content
Glama

osiris-mcp-server

An MCP server exposing Dutch university Osiris course catalogs as tools. It reads from the public, read-only Osiris Student API that universities publish at https://<host>.osiris-student.nl/ (e.g. TU/e, TU Delft, Radboud, Twente, Wageningen).

Quick start

Requires Python 3.11+ and uv.

uv sync
uv run osiris          # run the MCP server over stdio

The server targets TU/e (tue) by default and accepts an institution code per call, e.g. tue, tudelft, rug, ut:

search_courses("4DM00", institution="tudelft")
get_course("4DM00", institution="tue")

Related MCP server: University Course Catalog MCP Server

Usage with MCP clients

Add the server to your MCP client configuration.

{
  "mcpServers": {
    "osiris": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/osiris-mcp-server", "osiris"]
    }
  }
}

For clients that support environment variables (e.g. OSIRIS_LANGUAGE, OSIRIS_TIMEOUT), pass them via the env key.

Tools

Tool

Description

list_faculties()

Faculties/departments, code + name

list_course_types()

Course types (Bachelor College, Graduate School, ...)

get_available_years()

Academic years in the catalog

get_categories()

Course categories/levels

get_blocks()

Period/block tokens (e.g. 1, GS1, JAAR)

get_timeslots()

Timeslots (e.g. A, B1)

get_units()

Coordinating units (capaciteitsgroep/sectie)

get_lecturers()

Lecturer names

get_languages()

Instruction languages

get_education_types()

Education types (e.g. Challenge based)

get_course_structure()

Sections/fields a course contains

list_courses(faculty, course_type, ...)

{code, id, name, year} list for a faculty + type

get_course(code, year)

Course header (id, code, name, ECTS, course_type, owner, level, year, blocks)

get_course_details(code)

Full detail: content, exams, lecturers, materials, deep link, ...

get_course_details_by_id(id)

Full detail by internal course id

search_courses(keyword, ...)

Full-text search with filters

search_minors(keyword, ...)

Search minors (coherent packages, certificates)

get_minor_details(code)

Full minor detail: study programme, entrance requirements, ...

get_minor_details_by_id(id)

Full minor detail by internal minor id

The list_courses and search_courses tools accept the same filters; every one is optional except faculty for list_courses:

Filter

Description

faculty

Faculty code (e.g. EE) or full name

course_type

Course-type code or full name (default BC)

category

Category/level (e.g. Introductory, Advanced)

year

Start year (2025) or range (2025-2026)

period

Block token (e.g. 1 for bachelor Q1, GS1 for graduate Q1)

timeslot

Timeslot code, prefix match (e.g. A matches A1, A2, ...)

unit

Coordinating unit (capaciteitsgroep/sectie)

lecturer

Lecturer name

language

Instruction language (name or 2-letter code)

education_type

Education type (e.g. Challenge based)

available

Only courses with places available (true) or not (false)

Search results include a blocks list per course with the offering periods: block token, dates, timeslots, location and availability — plus lecturers, education_type, faculty and coordinating unit.

search_minors accepts the same year, faculty and education_type filters; results include startmoments (academic year + block the minor can be started) and study_programs (degree programmes the minor serves).

Configuration

Variable

Default

Purpose

OSIRIS_RELEASE_VERSION

(unset)

Optional search-index header

OSIRIS_MANIFEST

(unset)

Optional search-index header

OSIRIS_LANGUAGE

EN

Content language (NL or EN)

OSIRIS_TIMEOUT

30

HTTP timeout in seconds

Most institutions serve a sensible default index without OSIRIS_RELEASE_VERSION or OSIRIS_MANIFEST. Only set them to pin a specific Elasticsearch index version (e.g. one extracted from a university's Osiris app bundle).

Development

uv run pytest           # unit tests (offline)
uv run pytest -m e2e    # live tests against the configured catalog (default TU/e)
uv run ruff check .
uv run ruff format --check .
uv run mypy src

License

The code is MIT licensed; see LICENSE.

The course data comes from the relevant university's Osiris API and is owned by that institution. This project is not affiliated with or endorsed by any university, and the MIT license does not cover the underlying course data.

Available Tools

10 tools
get_available_yearsC

List academic years in the catalog.

Inputs: institution. Returns: result

get_categoriesC

List course categories/levels.

Inputs: institution. Returns: result

get_courseB

Get the header for a course code.

Args:
    code: course code, e.g. "4DM00".
    year: start year or range; most recent offering by default.
    institution: institution code (e.g. "tue"); defaults to the configured one.

Inputs: code (required), year, institution. Returns: result

get_course_detailsB

Get full course details (content, exams, lecturers, materials, ...).

Args:
    code: course code, e.g. "4DM00".
    institution: institution code (e.g. "tue"); defaults to the configured one.

Inputs: code (required), institution. Returns: result

get_course_details_by_idA

Get full course details by internal Osiris course id.

Args:
    id: internal course id, as returned by ``list_courses`` or
        ``search_courses``.
    institution: institution code (e.g. "tue"); defaults to the configured one.

Inputs: id (required), institution. Returns: result

get_course_structureB

Describe the sections and fields a course can contain.

Inputs: institution. Returns: result

list_coursesA

List course codes for a faculty and course type.

Args:
    faculty: faculty code, e.g. "EE".
    course_type: "BC" (Bachelor College) or "GS" (Graduate School).
    year: start year ("2025") or range ("2025-2026"); all years by default.
    institution: institution code (e.g. "tue"); defaults to the configured one.

Inputs: faculty (required), year, course_type, institution. Returns: result

list_course_typesC

List course types (e.g. Bachelor College, Graduate School).

Inputs: institution. Returns: result

list_facultiesC

List faculties (code + name).

Inputs: institution. Returns: result

search_coursesB

Search courses with optional filters.

Args:
    keyword: free-text search on code or name.
    faculty: filter by faculty code or full name.
    course_type: filter by course-type code or full name.
    category: filter by category/level.
    year: filter by academic year.
    limit: max results (1-100).
    offset: pagination offset.
    institution: institution code (e.g. "tue"); defaults to the configured one.

Inputs: year, limit, offset, faculty, +4 more. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.1/5.0

    Scored across 10 tools

    Disambiguation4/5

    Most tools are clearly distinct: list/get operations separate by resource (years, categories, faculties, course_types, courses). The main ambiguity is between get_course (header), get_course_details (full details), and get_course_details_by_id (full details by internal id) — get_course_details and get_course_details_by_id return the same data but keyed differently, and get_course's 'header' vs details boundary could cause misselection.

    Naming Consistency4/5

    The set follows a consistent get_/list_ verb prefix pattern (get_available_years, get_categories, list_courses, get_course, search_courses). Minor deviations exist: search_courses uses a different verb while other exploration tools use get_/list_, and get_course_details_by_id introduces the '_by_id' suffix that isn't used elsewhere.

    Tool Count4/5

    Ten tools is within the ideal range for a catalog/query server. Each tool earns a place: three discovery tools (years, categories, faculties/course_types), course listing/search, and course retrieval at different granularities.

    Completeness4/5

    The tool surface is strong for a read-only catalog: discovery (faculties, years, course types, categories), search, listing, and multi-level retrieval (header vs full details). The main gap is that get_course_details_by_id is redundant with get_course_by_code — agents could hit dead ends if they don't know which lookup path to use; a by-code variant of full details is also missing from the direct route.

    Related MCP Connectors

    Related MCP Servers