Skip to content

Repository files navigation

ntnu-api

Async Python client for NTNU course data. Wraps the three public, no-auth data sources for course information at the Norwegian University of Science and Technology behind one typed interface — built as a stepping stone toward an MCP server exposing course data to LLM agents.

Namespace Data Backing source
client.courses catalog search, per-course schedules & weekly timetables ntnu.no course-page JSON (Liferay)
client.grades grade distributions per course/year/semester HK-dir DBH statistics API
client.semesters term ids (26h/26v), teaching weeks, exam periods NTNU's TP timetable system

See docs/api-research.md for the full survey of NTNU data sources (including the dead and auth-gated ones) and the exact upstream request shapes.

Usage

import asyncio
from ntnu_api import NTNUClient


async def main() -> None:
    async with NTNUClient() as client:
        # Server-side course search (one 500-course page)
        page = await client.courses.search(2026, "objektorientert")
        for hit in page.courses[:3]:
            print(hit.course_code, hit.course_name)

        # Full catalog iteration
        async for result_page in client.courses.search_all(2026):
            ...

        # Dated teaching activities and the summarized weekly grid
        activities = await client.courses.schedules("TDT4100", 2026)
        grid = await client.courses.timetable("TDT4100", 2026)

        # Grade distribution (accepts bare or DBH-versioned codes)
        for row in await client.grades.distribution("TDT4100", years=[2023]):
            print(row.year, row.semester_name, row.grade, row.total)

        # Terms as known by NTNU's timetable system
        current = await client.semesters.current()


asyncio.run(main())

CLI

A small ntnu command wraps every client method for quick terminal poking:

uv run ntnu search 2026 maskinlæring        # catalog search (--all, --page, --sort)
uv run ntnu schedules TDT4100 2026          # dated activities, Oslo-local times
uv run ntnu timetable TDT4100 2026          # weekly grid
uv run ntnu grades TDT4100 --years 2023     # grade distribution table
uv run ntnu versions TDT4100                # DBH course-code versions
uv run ntnu semesters --current             # terms known by TP
uv run ntnu search 2026 ai --json | jq ...  # every subcommand takes --json

All models are pydantic v2; all errors derive from ntnu_api.NTNUAPIError (with NotFoundError, RateLimitError, APITimeoutError, ... subclasses). Transient failures (429, 502–504, connection errors) are retried with exponential backoff, honoring Retry-After.

A word of caution: the ntnu.no endpoints are NTNU's own internal AJAX surface, not a documented API — treat results accordingly and be polite (the client ships a descriptive User-Agent by default).

MCP server

The mcp extra ships an MCP server exposing the client as six tools (search_courses, get_course_schedule, get_weekly_timetable, get_grade_distribution, get_course_versions, get_semesters) with LLM-shaped responses: English names with Norwegian fallback, Oslo-local times, grade rows collapsed into per-term distribution tables with percentages, and an in-process TTL cache for the catalog and semester data.

uv sync --extra mcp                       # or: pip install 'ntnu-api[mcp]'
claude mcp add ntnu -- uv run ntnu-mcp    # register with Claude Code (stdio)
uv run ntnu-mcp --transport streamable-http --port 8000   # hostable HTTP service
npx @modelcontextprotocol/inspector uv run ntnu-mcp       # interactive testing UI

For Claude Desktop, add to claude_desktop_config.json:

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

Stack

  • uv — dependency & virtualenv management
  • httpx + pydantic v2 — transport and models
  • ruff — linting + formatting
  • ty — static type checking
  • pytest + pytest-asyncio + respx — testing
  • mise — pins Python/uv and exposes task shortcuts
  • Dev Container — reproducible environment

Development

Inside the Dev Container (or after uv sync on the host):

Task Command
mise test uv run pytest
mise lint uv run ruff check . && uv run ruff format --check .
mise fmt uv run ruff format . && uv run ruff check --fix .
mise typecheck uv run ty check
mise check lint + typecheck + test

The default test run is fully mocked (respx) against real captured payloads in tests/fixtures/. To also verify against the live endpoints:

NTNU_API_LIVE=1 uv run pytest tests/test_live.py

Roadmap

  • MCP server exposing the client as tools (ntnu-mcp)
  • Expose the remaining catalog search facets (campus, level, faculty/institute)
  • Course detail scraping (description, credits, prerequisites — HTML-only upstream)
  • TP emner.php semester catalog (English names, campus ids — see docs/api-research.md)
  • TP iCal export as a secondary timetable source once semesters publish

About

TypeScript client for NTNU course data (catalog search, schedules, timetables, grade statistics, semesters)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages