ntulearn-mcp
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., "@ntulearn-mcpWhat's due in NTULearn over the next two weeks?"
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.
ntulearn-mcp
MCP server for NTULearn (NTU Singapore's Blackboard Learn instance). Lets Claude Desktop, Claude Code, Prime Agent, Cursor, Cline, and other MCP hosts answer questions about your courses, announcements, calendar, and grades — and organise course files into a folder hierarchy on disk.
The shipped server is a single Rust binary built on
ultrafast-mcp, with 21 tools, 2 prompt templates,
and a course-resource URI. No Python, no pip, no virtualenv.
Quick start
cd rust
cargo build --release
# binary: rust/target/release/ntulearn-mcpOne-time cookie setup — just log in:
rust/target/release/ntulearn-mcp setupsetup opens NTULearn in a throwaway-profile browser (Chrome, Arc, Brave, Edge, or Chromium) and
watches the browser's own debugging channel for your BbRouter session cookie. You only log in in
the window that opens — no copy-paste, no devtools, no OS-keychain prompts. The captured cookie is
validated live against the API before it is saved to <config>/ntulearn-mcp/cookie. If no supported
browser is found (e.g. a headless server), it falls back to a one-time paste.
Verify:
rust/target/release/ntulearn-mcp check # expect: Cookie source + Live validity : OK (200)Register the Rust binary with your MCP host (adjust the path):
# e.g. Claude Code / Cursor / Cline — point the command at the Rust binary:
"/Users/you/ntulearn-mcp/rust/target/release/ntulearn-mcp"The server never touches your OS keychain
Cookie resolution is strictly: NTULEARN_COOKIE env var → config file
(<config>/ntulearn-mcp/cookie) → read-only Firefox cookies.sqlite. No keychain reads, no
password dialogs, no security commands. The setup command acquires a fresh cookie through a
throwaway browser's debugging port — keychain-free by construction.
Session expiry
When NTULearn rejects a call with 401, the server re-resolves the cookie (env → config → Firefox),
persists a working value to the config file, and retries the call once. Refresh is never
proactive; it happens on a live 401 or when you run ntulearn-mcp refresh.
Related MCP server: canvas-lms
Install from source
git clone https://github.com/gangula-karthik/ntulearn-mcp.git
cd ntulearn-mcp/rust
cargo build --release # -> target/release/ntulearn-mcp
cargo test # 30 unit tests
target/release/ntulearn-mcp # serve over stdioWhat it's for
Four prompts this server is built to make easy:
"What announcements happened across my courses this week?" → fans out across all enrolled courses, sorted newest first.
"What assignments do I have due next week?" → reads NTULearn's calendar, including gradable items (
type=GradebookColumn)."Organise this semester's NTULearn content into
~/NTU/y3s1/sc2002/week 8/…on my disk." → walks the course tree and downloads files into a folder layout you describe in plain English."Pull the assignment due dates and grading weightages out of this course briefing PDF." → reads small text-heavy documents inline (no filesystem hop).
For multi-page, diagram-heavy lecture decks, use download_file and drag the PDF into claude.ai
— that path has a native vision rendering budget. MCP tool results are capped at 1 MB; this server
doesn't try to compete with drag-and-drop for full lecture decks.
Tools
21 tools. Most do cross-course aggregation by default — you almost never need to pass course IDs by hand.
Tool | What it does |
| List enrolled courses. |
| Walk a course's content tree. Omit |
| Recursive substring search within one course. |
| Calendar items across enrolled courses. Defaults to the next 2 weeks. |
| Announcements across enrolled courses, newest first. Optional |
| Gradebook columns across enrolled courses, with your scores when available. |
| Download every file on a content item to disk. |
| Read an attached file's content inline (no filesystem hop). |
| List your NTULearn mailbox messages (inbox/sent). |
| Read one message by ID, with full body and recipients. |
| List users in a course (instructors, TAs, students). |
| List the groups defined in a course (tutorial/lab groups). |
| List the members of a specific course group. |
| List submission attempts for an assignment (gradebook column). |
| Search content across all courses at once; results carry courseId + breadcrumb. |
| Return one course's entire content tree as nested JSON (bounded by |
| Recursively download every file in a course to |
| One-call digest: announcements + upcoming + gradebook summary since a cutoff. |
| Export calendar items (incl. due dates) as an iCalendar |
| Export your gradebook as a CSV string for a spreadsheet. |
| Bite-size briefing of one course: instructors, upcoming, announcements, grades, top folders. |
Most read-only tools default to response_format='json'; pass response_format='markdown' for a
human-readable summary. List-returning tools accept limit/offset pagination.
Known environment limits (observed on real NTULearn)
list_messages/read_message— the public/users/me/messagesREST API is not exposed on this NTU instance (404), so the Rust client instead walks the internal v1 conversation API (one cached mailbox flatten → per-courseconversations), which returns the same parity-shaped message objects. Recipients are derived from the conversation (includesAllMembers, member groups, and resolved participant ids). Thesentfolder is inferred from the sender being you.get_group_members— the public/courses/{id}/groups/{id}/usersendpoint returns 403 for student accounts, so the client uses the internal v1memberships?groupId=…&expand=user,courseRoleendpoint (accessible to students); member names and full role names (S→Student, T→TA, P→Primary instructor, …) are rendered from the expandeduserobjects.Calendar/upcoming windows wider than ~16 weeks → NTULearn rejects with a 400
courseErrorsentry; keepsince/untilwithin a semester.read_file_contentextracts text from simple documents; for large/graphical PDFs preferdownload_fileand read the file in the client.
Resources & Prompts
Resources —
ntulearn://courses/{course_id}returns a JSON course briefing (the same content asntulearn_summarize_course).list_resourcesenumerates your enrolled courses, and the{course_id}URI template lets clients read any course directly.Prompts —
ntulearn-weekly-brief— argscourses(optional comma-separated IDs) anddays(default 7). Produces a prompt that chainsntulearn_get_announcements+ntulearn_get_upcomingover a computed since/until window.ntulearn-assignment-triage— argscoursesanddays(default 14). Produces a due-date triage prompt that chainsntulearn_get_upcoming(type='GradebookColumn')+ntulearn_get_gradebook.
Example prompts
"What announcements went out across my courses this past week?" —
get_announcements(since='...')."What assignments do I have due in the next two weeks?" —
get_upcoming(type='GradebookColumn')."Show me the full calendar for the next 10 days." —
get_upcoming(until='...')."What's my current grade in
_12345_1?" —get_gradebook(course_ids=['_12345_1'])."Walk my enrolled courses and put each course's content under
~/NTU/y3s1/<course-name>/<topic>/…." — chainslist_courses→get_course_contents→download_file.
Authentication
The server resolves the BbRouter cookie strictly in this order:
NTULEARN_COOKIEenv varConfig file at
<config>/ntulearn-mcp/cookie(written bysetup,refresh, or a successful 401-refresh)Firefox
cookies.sqlite(read-only, plaintext — no keychain decryption)
Never proactive: the 401-refresh path runs only after a live 401. Run ntulearn-mcp check to see
the current source, expiry, and live validity.
The setup command
ntulearn-mcp setup is the normal first-run path. It:
Checks for an existing cookie (env → config → Firefox) and validates it live. If valid: done.
Otherwise launches Chromium (Chrome/Arc/Brave/Edge) with a throwaway profile + local debugging port, opens NTULearn, and polls the DevTools protocol for the
BbRoutercookie (15-minute login timeout). You just log in.Validates the captured cookie live (
GET /learn/api/public/v1/users/memust return 200) before saving it to the config file. A pre-login guest cookie is rejected, not saved.Cleans up the browser window + profile automatically.
No supported browser → falls back to a one-time paste (accepts a bare value, BbRouter=..., or a
full Cookie: header).
Manual cookie fallback
Open https://ntulearn.ntu.edu.sg in your browser and log in.
DevTools (
F12) → Application → Cookies →ntulearn.ntu.edu.sg.Copy the Value of the
BbRoutercookie (starts withexpires:).Either run
ntulearn-mcp setupand choose paste, or add it to your MCP host'senvblock:{ "mcpServers": { "ntulearn": { "command": "/Users/you/ntulearn-mcp/rust/target/release/ntulearn-mcp", "args": [], "env": { "NTULEARN_COOKIE": "expires:1234567890,id:..." } } } }Restart your MCP host.
The cookie expires with your NTULearn session (days–weeks); re-run setup (or let the 401-refresh
resolve it) when it does.
Optional configuration
Env var | Default | Purpose |
| — | Manual cookie fallback. |
|
| Change for a different Blackboard instance. |
|
| Default |
|
| SQLite persistence path for the response cache (best-effort; falls back to in-memory). |
|
|
|
Development
cd rust
cargo test # 30 unit tests (cache, client, cookie, capture, setup)
cargo build --releaseProject layout (rust/crates/ntulearn-mcp/src/):
main.rs # ultrafast-mcp server wiring (stdio + setup/check/refresh CLI)
handlers.rs # the 21 tool handlers
parsers.rs # HTML body → download URL extraction (scraper)
render.rs # markdown/csv/ics renderers
client.rs # reqwest (HTTP/2, retries) Blackboard REST client + download
cache.rs # SQLite + in-memory TTL cache (per-instance LRU, scoped keys)
cookie.rs # layered cookie resolution (never keychain)
setup.rs # setup / check / refresh subcommands
capture.rs # throwaway-browser CDP cookie capture
resources.rs # course resource template + reader
prompts.rs # prompt templates
schemas.rs # auto-generated tool schemas
tools.rs # tool definition registryPublishing / secret check
Before pushing to a remote, verify no credentials are in history or the tree:
git status --short # no .env / downloads / cookie files staged
git log --all -p | grep -nE '(BbRouter=|expires:[0-9]{10,},id:|Set-Cookie|ghp_[A-Za-z0-9]{20,})' | grep -viE 'test|example|README' || echo "clean"If anything real shows up, do not push — scrub it first (rotate the credential, remove it from history with filter-repo or an interactive rebase).
Troubleshooting
"No NTULearn cookie found" / tools fail with 401.
Run ntulearn-mcp check to see the cookie source and live validity, then ntulearn-mcp setup to
re-capture (or paste) a fresh cookie.
MCP host lists "ntulearn" but tool calls return nothing.
Make sure the server is started with the config file present (run setup once), and confirm it works
directly: target/release/ntulearn-mcp check.
Prime Agent lists the server but tools aren't available.
Run /reload in Prime Agent (or restart it) so settings are re-read.
read_file_content returns "No download URL found" for an item.
That content node is not a file (e.g. a text page or tool link). For real attached files use
download_file with a destination_dir.
The server crashes on startup.
rust/target/release/ntulearn-mcpMost common cause: no cookie resolvable. The error message will guide you (or run setup).
Disclaimer & responsible use
Use at your own risk. This is an unofficial, personal-use tool. It is not affiliated with, endorsed by, or sponsored by NTU Singapore, Anthology Inc., or Blackboard Learn. NTULearn, Blackboard, and related marks belong to their respective owners.
Your account, your responsibility. Driving the LMS via your session cookie may be inconsistent with NTU's IT acceptable use policy or terms of service. Consult NTU policy if you're unsure.
Your cookie stays local. The
BbRoutercookie is read locally and sent only tontulearn.ntu.edu.sg. The author never sees it.Don't share cookie values. Anyone with your
BbRoutercan act as you on NTULearn until it expires.Don't run this on someone else's behalf. Each user should run their own instance against their own account.
The MIT license disclaims all warranties — see LICENSE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
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.
Related MCP Connectors
MCP server for the Inistate platform: module discovery, entry management, and activity submission.
The Academy curriculum as an offline MCP library. Hosted course connector adds progress.
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
Remote MCP server for training, nutrition, wellness, and performance data with OAuth 2.0.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables interaction with D2L Brightspace through the MCP protocol, supporting multi-strategy authentication and opt-in write operations.11410MIT
- AlicenseNot gradedqualityDmaintenanceMCP server for Canvas LMS with automatic OAuth authentication. Enables interaction with courses, assignments, grades, modules, discussions, quizzes, files, calendar, messaging, and more without manual API token management.44MIT
- AlicenseNot gradedqualityBmaintenanceMCP server that provides tools to interact with SNU eTL (Canvas LMS), including viewing courses, assignments, announcements, grades, downloading and organizing course files, with persistent local storage and automatic sync.MIT
- AlicenseNot gradedqualityBmaintenanceProvides MCP tools to read SJTU Canvas data, download course files, and access video/subtitle resources without authentication simulation.MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- 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/gangula-karthik/ntulearn-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server