1. Conduid
  2. Documentation
  3. Ntulearn MCP
MCP server · Documentation

Ntulearn MCP

MCP server for NTULearn (NTU Blackboard): read-only course/announcement/calendar/grade tools + course file download. Fast: HTTP/2, caching, 21 tools.

Unclaimed docs
34Low

Scored a month ago · breakdown

About Ntulearn MCP

Ntulearn MCP is an MCP server published by gangula-karthik in the Documentation category: mCP server for NTULearn (NTU Blackboard): read-only course/announcement/calendar/grade tools + course file download. Fast: HTTP/2, caching, 21 tools.

The repository has 5 stars and 1 forks, with the last commit a month ago. Recent commit activity is the strongest signal that reported issues will get attention.

Install

From source
git clone https://github.com/gangula-karthik/ntulearn-mcp

This server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.

Tools (21)

ntulearn_download_courseRecursively download every file in a course to `~/Downloads/NTU/<course>`.

Parameters not published.

ntulearn_download_fileDownload every file on a content item to disk. `destination_dir` builds hierarchies.

Parameters not published.

ntulearn_export_calendar_icsExport calendar items as an iCalendar `.ics` string.

Parameters not published.

ntulearn_export_gradebook_csvExport your gradebook as a CSV string.

Parameters not published.

ntulearn_get_announcementsAnnouncements across enrolled courses, newest first. Optional `since`.

Parameters not published.

ntulearn_get_content_treeOne course's entire content tree as nested JSON (bounded by `max_depth`).

Parameters not published.

ntulearn_get_course_contentsWalk a course's content tree. Omit `parent_id` for the top level; pass it to drill into a folder.

Parameters not published.

ntulearn_get_gradebookGradebook columns across enrolled courses, with your scores when available.

Parameters not published.

ntulearn_get_gradebook_attemptsList submission attempts for an assignment.

Parameters not published.

ntulearn_get_group_membersList the members of a course group.

Parameters not published.

Show all 21 tools
ntulearn_get_upcomingCalendar items across enrolled courses. Defaults to the next 2 weeks. `type='GradebookColumn'` filters to assignments.

Parameters not published.

ntulearn_list_course_groupsList the groups defined in a course.

Parameters not published.

ntulearn_list_course_usersList users in a course (instructors, TAs, students).

Parameters not published.

ntulearn_list_coursesList enrolled courses.

Parameters not published.

ntulearn_list_messagesList mailbox messages (inbox/sent).

Parameters not published.

ntulearn_read_file_contentRead an attached file's content inline (no filesystem hop).

Parameters not published.

ntulearn_read_messageRead one message by ID, with full body and recipients.

Parameters not published.

ntulearn_search_all_coursesSearch content across **all** courses; results carry courseId + breadcrumb.

Parameters not published.

ntulearn_search_course_contentRecursive substring search within one course.

Parameters not published.

ntulearn_summarize_courseBriefing for one course: instructors, upcoming, announcements, grades, top folders.

Parameters not published.

ntulearn_whats_newOne-call digest: announcements + upcoming + gradebook since a cutoff.

Parameters not published.

Ask AI

✦

Ask AI about Ntulearn MCP

Powered by Claude · Grounded in docs

I know everything about Ntulearn MCP. Ask me about installation, configuration, usage, or troubleshooting.

Security checks

Static checks derived from the repository 4 days ago. Heuristics on the source, not an audit.

  • ✓README presentThe repository ships documentation.
  • ✓License declaredUsable under a stated license.
  • !Tests presentNo tests found. Behaviour changes may go unnoticed between versions.
  • ✓Dependencies pinnedA lockfile pins dependency versions.
  • ✓No dynamic code executionSource scan found no eval, dynamic Function, or shell execution.

1 check has not been run yet.

Releases

v0.7.1v0.7.1 · 10 Sep 2026Full Changelog: https://github.com/gangula-karthik/ntulearn-mcp/compare/v0.7.0...v0.7.1
v0.7.0v0.7.0 · 10 Sep 2026Full Changelog: https://github.com/gangula-karthik/ntulearn-mcp/commits/v0.7.0

README

ntulearn-mcp

MCP server + agent-first CLI for NTULearn — NTU Singapore's Blackboard Learn instance. Point any MCP host (Claude Desktop, Claude Code, Cursor, Cline, Prime Agent) at it to ask about your courses, announcements, calendar, and grades — and to 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.

Website: https://ntulearn-mcp.krtk.app


Quick start

git clone https://github.com/gangula-karthik/ntulearn-mcp.git
cd ntulearn-mcp/rust
cargo build --release          # binary: rust/target/release/ntulearn-mcp

Log in once — setup opens NTULearn in a throwaway-profile browser and captures your session cookie for you. No copy-paste, no devtools, no keychain prompts.

rust/target/release/ntulearn-mcp setup
rust/target/release/ntulearn-mcp check   # expect: Live validity : OK (200)

Register the binary with your MCP host:

{
  "mcpServers": {
    "ntulearn": {
      "command": "/Users/you/ntulearn-mcp/rust/target/release/ntulearn-mcp",
      "args": []
    }
  }
}

That's it. The same binary serves MCP with no arguments and acts as a CLI with a command.


Why the CLI: about 64x fewer tokens

MCP hosts inject every tool's JSON schema into context every turn — about 6.6k tokens for this server's 21 tools. The same binary doubles as a plain CLI, whose terse tabular output an agent reads in a few hundred tokens:

ntulearn-mcp courses                 # 6 courses ≈ 100 tokens (MCP tool result: ≈ 450)
ntulearn-mcp tree _2706844_1         # a course's content tree
ntulearn-mcp whats-new --days 7      # announcements + due dates + grades digest
ntulearn-mcp multi "courses" "grades" "upcoming --type GradebookColumn"   # concurrent

Measured with the built-in benchmark (ntulearn-mcp selfbench):

Metric MCP CLI Improvement
Discovery tokens (per conversation) 6,639 (every turn) 586 (once, on demand) 11.3x
Task output tokens (4-task suite) 2,193 492 (compact TSV) 4.5x
10-turn conversation model ~68,583 tok ~1,078 tok 63.6x
courses cold (2 RTTs, 25ms mock) — 59ms 3.4x vs v0.3
courses warm (SQLite cache) — 82µs —
4 commands concurrently (multi) 225ms sequential 59ms (singleflight-deduped) 3.8x

The CLI shares the same handlers, cache, and cookie lifecycle as the MCP server. Point your agent at the binary and have it run ntulearn-mcp help (≈590 tokens, once) instead of registering 21 tools.


What people ask for

  1. "What announcements happened across my courses this week?" — fans out across all enrolled courses, newest first.
  2. "What assignments are due next week?" — reads the calendar, including gradable items.
  3. "Organise this semester's content into ~/NTU/y3s1/sc2002/week 8/…." — walks the course tree and downloads into any folder layout you describe.
  4. "Pull the due dates and grade weightages out of this course briefing PDF." — reads small text-heavy documents inline.

For multi-page, diagram-heavy lecture decks, use download_file and open the PDF in your client — MCP tool results are capped at 1 MB, so this server does not compete with drag-and-drop for full decks.


CLI reference

ntulearn-mcp help prints this list; ntulearn-mcp help <cmd> shows one command's flags.

courses                                  my enrolled courses (run this first for course ids)
contents     course-id                   one level of a course's content tree
tree         course-id                   entire content tree of a course in one call
search       course-id query             search one course's content tree
find         query                       search content across ALL courses
dl           course-id content-id        download a content item's files to disk
read         course-id content-id        read a content item's files inline (pdf/office/text)
upcoming                                 calendar items + due dates across courses
announcements                            announcements across courses, newest first
grades                                   gradebook columns + your grades across courses
attempts     course-id column-id         attempts on one gradebook column
messages                                 course messages (inbox/sent)
message      message-id                  read one course message
users        course-id                   users enrolled in a course
groups       course-id                   groups in a course
members      course-id group-id          members of one group
summary      course-id                   one-shot course digest (staff, contents, dates)
whats-new                                curated digest of everything new since a cutoff
dl-course    course-id                   bulk-download a course's files
ics                                      export calendar items to an .ics file
grades-csv                               export gradebook to .csv

Output is compact TSV by default. Flags: --json, --md, --toon, --fields a,b, --max-chars N, --full, --limit N, --offset N. Every MCP tool argument is also accepted as a kebab-case flag (--include-disabled, --course-ids a,b, --mode vision).

multi runs several commands concurrently in one process, deduplicating identical in-flight HTTP fetches and sharing one cache:

ntulearn-mcp multi "courses" "announcements --since 2026-01-01" "grades"
# results arrive concatenated, each prefixed with: ===== <command> =====

Output formats

Format Flag Notes
Compact TSV default Smallest; field-capped, no nesting.
Markdown --md Human-readable.
JSON --json Raw payload.
TOON --toon Smallest full-fidelity format — 40% fewer tokens than pretty JSON (52% on large lists).

TOON (Token-Oriented Object Notation, spec v4.1) declares array shapes once and drops repeated keys, so the savings scale with how tabular the payload is. Every MCP tool accepts response_format: "toon" too.


Tools

21 tools, most doing cross-course aggregation by default — you rarely pass course IDs by hand.

Tool What it does
ntulearn_list_courses List enrolled courses.
ntulearn_get_course_contents Walk a course's content tree. Omit parent_id for the top level; pass it to drill into a folder.
ntulearn_search_course_content Recursive substring search within one course.
ntulearn_get_upcoming Calendar items across enrolled courses. Defaults to the next 2 weeks. type='GradebookColumn' filters to assignments.
ntulearn_get_announcements Announcements across enrolled courses, newest first. Optional since.
ntulearn_get_gradebook Gradebook columns across enrolled courses, with your scores when available.
ntulearn_download_file Download every file on a content item to disk. destination_dir builds hierarchies.
ntulearn_read_file_content Read an attached file's content inline (no filesystem hop).
ntulearn_list_messages List mailbox messages (inbox/sent).
ntulearn_read_message Read one message by ID, with full body and recipients.
ntulearn_list_course_users List users in a course (instructors, TAs, students).
ntulearn_list_course_groups List the groups defined in a course.
ntulearn_get_group_members List the members of a course group.
ntulearn_get_gradebook_attempts List submission attempts for an assignment.
ntulearn_search_all_courses Search content across all courses; results carry courseId + breadcrumb.
ntulearn_get_content_tree One course's entire content tree as nested JSON (bounded by max_depth).
ntulearn_download_course Recursively download every file in a course to ~/Downloads/NTU/<course>.
ntulearn_whats_new One-call digest: announcements + upcoming + gradebook since a cutoff.
ntulearn_export_calendar_ics Export calendar items as an iCalendar .ics string.
ntulearn_export_gradebook_csv Export your gradebook as a CSV string.
ntulearn_summarize_course Briefing for one course: instructors, upcoming, announcements, grades, top folders.

Read-only tools default to response_format='json'; pass 'markdown' for a readable summary. List-returning tools accept limit/offset. A resource URI, ntulearn://courses/{course_id}, returns a JSON course briefing, and two prompts (ntulearn-weekly-brief, ntulearn-assignment-triage) chain the tools for you.

Known environment limits

  • list_messages / read_message — the public /users/me/messages REST API returns 404 on this instance, so the client walks the internal v1 conversation API instead (one cached mailbox flatten → per-course conversations) and derives recipients from the conversation.
  • get_group_members — the public groups endpoint returns 403 for student accounts, so the client uses the internal v1 memberships endpoint instead.
  • Calendar windows wider than ~16 weeks are rejected by NTULearn with a 400 courseErrors entry — keep since/until within a semester.
  • read_file_content extracts text from simple documents; for large or graphical PDFs, use download_file and open the file in your client.

Authentication

The BbRouter cookie is resolved strictly in this order:

  1. NTULEARN_COOKIE environment variable
  2. Config file at <config>/ntulearn-mcp/cookie
  3. Firefox cookies.sqlite (read-only, plaintext)

No keychain access, ever — no password dialogs, no security commands. When NTULearn rejects a call with 401, the server re-resolves the cookie, persists a working value, and retries once. Refresh is never proactive; it happens on a live 401 or when you run ntulearn-mcp refresh.

setup is the normal first-run path: it validates any existing cookie live, and if there is none, opens Chrome/Arc/Brave/Edge/Chromium with a throwaway profile and polls the DevTools protocol for your cookie (15-minute login timeout). The captured value is validated against the API before it is saved, then the browser and profile are cleaned up. On a headless server with no supported browser, it falls back to a one-time paste.

The cookie expires with your NTULearn session (days to weeks). Re-run setup when it does.

  1. Log in at https://ntulearn.ntu.edu.sg.

  2. DevTools (F12) → Application → Cookies → ntulearn.ntu.edu.sg.

  3. Copy the Value of the BbRouter cookie (starts with expires:).

  4. Run ntulearn-mcp setup and choose paste, or set it in your MCP host's env block:

    { "env": { "NTULEARN_COOKIE": "expires:1234567890,id:..." } }
    
  5. Restart your MCP host.


Configuration

All optional — the defaults work for most users.

Env var Default Purpose
NTULEARN_COOKIE — Manual cookie fallback.
NTULEARN_BASE_URL https://ntulearn.ntu.edu.sg Change for a different Blackboard instance.
NTULEARN_DOWNLOAD_DIR ./downloads Default destination_dir for download_file / download_course.
NTULEARN_CACHE_DIR ~/.cache/ntulearn-mcp/cache.sqlite3 SQLite response cache (falls back to in-memory).
NTULEARN_CACHE_MODE readwrite readwrite, readonly, or off.
NTULEARN_MCP_AUTOUPDATE ask on, off, or ask.
NTULEARN_MCP_NO_UPDATE_CHECK — Set to 1 to disable update checks.
NTULEARN_MCP_UPDATE_INTERVAL_SECS 86400 Check throttle.
NTULEARN_MCP_REPO upstream repo Override for forks.
NTULEARN_MCP_REQUIRE_SIGNATURE — Set to 1 to refuse unsigned releases.

Updates

The binary checks GitHub for a newer release at most once every 24 hours, and never blocks or breaks the server. When one is available you get a one-line notice in the MCP host's log and at the start of the session.

ntulearn-mcp update            # check + prompt before installing
ntulearn-mcp update --check    # exit 0 = current, 10 = update available
ntulearn-mcp update --yes      # install without prompting
ntulearn-mcp update --auto on  # background-install new releases
ntulearn-mcp update --channel beta   # try prereleases first

With --auto on the server downloads the release in the background, verifies its sha256 against the release's checksums.txt, and atomically swaps the binary — the new version activates when your MCP host next restarts the server. Every checksums.txt is minisign-signed by CI, and the public key is committed as minisign.pub and baked into the binary, so a tampered release is refused. Set require_signature: true in settings.json to refuse unsigned releases outright. Installs the binary did not make itself (cargo, Homebrew, dev builds) are detected and answered with the right update command instead of a self-replace. A Homebrew formula template lives in packaging/homebrew/.

Cutting a release (maintainers): bump rust/Cargo.toml, tag v<version>, push. The workflow builds Linux, macOS (arm64 + x64), and Windows binaries, and publishes checksums.txt with its minisign signature. Tags with a hyphen (e.g. v0.8.0-rc.1) publish as prereleases automatically.


Troubleshooting

  • "No NTULearn cookie found", or tools fail with 401 — run ntulearn-mcp check to see the cookie source and live validity, then ntulearn-mcp setup to re-capture one.
  • MCP host lists the server but tool calls return nothing — run rust/target/release/ntulearn-mcp directly. The usual cause is an unresolvable cookie; the error message will say so.
  • Prime Agent lists the server but tools aren't available — run /reload (or restart it) so settings are re-read.
  • read_file_content returns "No download URL found" — that content node is not a file (a text page or tool link, say). Use download_file for real attached files.

Development

cd rust
cargo test                              # 53 unit tests
cargo build --release
target/release/ntulearn-mcp selfbench   # token + latency + concurrency benchmark

Layout (rust/crates/ntulearn-mcp/src/):

main.rs      no args → MCP stdio server; command → CLI; setup/check/refresh
cli.rs       the 21 commands, compact TSV renderer, `multi` concurrency
handlers.rs  the 21 tool handlers (shared by MCP server and CLI)
bench.rs     selfbench + mock Blackboard API
client.rs    reqwest (HTTP/2, retries, parallel fan-out, singleflight dedup)
cache.rs     SQLite (WAL, persistent conn) + in-memory TTL cache
cookie.rs    layered cookie resolution (never keychain)
setup.rs     setup / check / refresh subcommands
capture.rs   throwaway-browser CDP cookie capture
parsers.rs   HTML body → download URL extraction
render.rs    markdown / csv / ics renderers
resources.rs course resource template + reader
prompts.rs   prompt templates
schemas.rs   tool schemas
tools.rs     tool definition registry

Before pushing, check that no credential is staged or in history:

git status --short
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. Rotate the credential first.


Disclaimer

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.

  • Your account, your responsibility. Driving the LMS with your session cookie may be inconsistent with NTU's acceptable use policy. Check NTU policy if you are unsure.
  • Your cookie stays local. It is read locally and sent only to ntulearn.ntu.edu.sg.
  • Don't share cookie values. Anyone with your BbRouter can act as you until it expires.
  • Don't run this for someone else. Each user should run their own instance against their own account.

MIT licensed — see LICENSE. All warranties are disclaimed.

README fetched from the repository 4 days ago. The original is authoritative.

Questions

About Ntulearn MCP

How do I install Ntulearn MCP?

No package has been published for this server, so install it from source: clone the repository and follow its README.

Is Ntulearn MCP safe to use with an AI agent?

Its trust score is 34 out of 100 (low). It passes 4 of the 5 static security checks run so far; the failures are listed above. It has no ConduID identity yet, so agent calls to it are not receipted.

Is Ntulearn MCP still maintained?

Yes — the latest release is v0.7.1 (10 Sep 2026), and the last commit was a month ago. The repository has 5 stars.