ntnu-api
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ntnu-apisearch courses for objektorientert in 2026"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 |
| catalog search, per-course schedules & weekly timetables | ntnu.no course-page JSON (Liferay) |
| grade distributions per course/year/semester | HK-dir DBH statistics API |
| term ids ( | 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 --jsonAll 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 UIFor 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
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 |
|
|
|
|
|
|
|
|
| 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.pyRoadmap
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.phpsemester catalog (English names, campus ids — see docs/api-research.md)TP iCal export as a secondary timetable source once semesters publish
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/MartinSA04/ntnu-api'
If you have feedback or need assistance with the MCP directory API, please join our Discord server