ntulearn-mcp
Click on "Deploy 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-mcp整理我这周所有课程的新公告。"
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.
NTU MCP
面向南洋理工大学(NTU Singapore)学生的非官方 MCP 服务,将 NTULearn(Blackboard Learn)的课程、公告、日历、成绩和课程文件接入支持 MCP 的 AI 客户端。
连接 NTULearn 与 AI 助手,集中查询课程公告、作业截止时间和成绩,并将课程资料增量同步到本地。共提供 10 个工具,默认从 Chrome 读取登录会话。
能做什么
你可以向已连接的 AI 客户端提出:
“整理我这周所有课程的新公告。”
“列出未来两周的作业、截止时间和来源;有冲突的日期单独标注。”
“把本学期课程资料按课程和原有文件夹同步到本地,先给我预览。”
“读取这份作业说明,找出提交要求和评分占比。”
“查询这门课已经公布的成绩。”
服务只读取学校端数据,不提交作业、不修改成绩,也不自动安排提醒。下载和同步工具会写入你指定的本地目录。
Related MCP server: ZHAW Moodle MCP Server
环境要求
Python 3.12 或更高版本。
已安装 uv 和 Git。
具有 NTULearn 访问权限,并已在 Chrome 中完成 SSO / MFA 登录。
支持本地
stdioMCP 服务的客户端。
资料增量同步使用 fcntl 文件锁,适用于 Linux / macOS;当前实现不支持在原生 Windows 上执行实际同步。其他工具仍受浏览器 Cookie 读取能力和系统密钥环配置影响。
快速开始
1. 下载并安装本仓库
git clone https://github.com/Realsbt/ntu-mcp.git
cd ntu-mcp
uv sync --frozen请使用本仓库源码启动,以包含新增功能;直接执行 uvx ntulearn-mcp 获取的是包索引上的同名发行版,不保证与本仓库一致。
2. 登录 NTULearn
在 Chrome 中打开 NTULearn,完成登录。服务会尝试自动读取本机的 BbRouter Cookie,通常无需手动复制。
3. 配置 MCP 客户端
在客户端的 MCP 配置中加入以下内容,将 /absolute/path/to/ntu-mcp 替换为克隆目录的绝对路径。如果客户端找不到 uv,也应将 command 改为它的绝对路径。
{
"mcpServers": {
"ntulearn": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/ntu-mcp",
"run",
"ntulearn-mcp"
]
}
}
}例如,Cursor 可使用 ~/.cursor/mcp.json。其他客户端按各自的 MCP 设置入口填写相同的启动命令与参数。
4. 重启客户端并试用
先尝试:“列出我的 NTULearn 课程。”再查询公告或作业。首次访问可能需要允许系统密钥环读取。
工具列表
工具 | 用途 |
| 列出已选课程。 |
| 查看课程内容;使用 |
| 在指定课程中递归搜索内容。 |
| 汇总课程日历,默认查询未来两周;可用 |
| 汇总课程公告,按时间从新到旧排列;可指定 |
| 查询成绩簿项目,以及学校已开放的个人成绩。 |
| 下载内容项的附件,可指定本地 |
| 直接读取附件内容;PDF 默认提取文字,可用 |
| 预览或执行课程资料增量同步,保留本地修改。 |
| 汇总作业、截止时间、原始提交状态和来源,并提供待核实的文档日期线索。 |
支持跨课程汇总的工具通常可以省略 course_ids。单课程内容查询仍需提供 course_id;以客户端展示的工具参数为准。
课程资料增量同步
调用 ntulearn_sync_materials 时,必须提供 semester 和 destination_dir,可以用 course_ids 限定课程。semester 是本地目录标签。
先预览:
{
"semester": "2026S1",
"destination_dir": "~/NTU",
"course_ids": ["_course_1"],
"dry_run": true
}确认预览后,以相同参数将 dry_run 改为 false 即可下载。默认值是 true,不会自动开始下载。
文件按“学期 / 课程 / 原有文件夹”组织,文件名和课程目录附带稳定短标识,降低重名冲突。同步记录保存在 .ntulearn-manifest.json,记录路径、哈希和时间,不存储登录凭据或带签名的下载链接。
已有记录且未变化的文件会跳过;失败文件在下次运行时重试。
本地修改会标记为
local_conflict并保留;工具不会删除文件。课程或文件夹改名后,已跟踪文件保留原有本地路径。
verify_remote: true会重新下载并比较哈希,即使远程修改时间未变;没有远程修改时间时也会重新下载比较。每个成功文件及其同步记录分别通过原子写入保存,支持按文件继续同步;不支持单个文件的字节断点续传。
文件锁防止同一目标目录的同步任务并发写入,但外部编辑器不受该锁约束。
单文件上限为 100 MiB;每门课 max_items 默认 500、最大 2000,目录遍历深度上限 15。完成后应检查 errors、truncated 和各文件状态;一次调用结束不代表所有资料均已同步。
作业统一汇总
ntulearn_get_assignments 合并成绩簿、日历考核项和作业页面的信息:
{
"include_documents": true,
"max_documents": 10,
"max_items": 500
}也可添加 course_ids 限定课程。跨来源合并依据关联 ID 或忽略大小写后完全相同的标题,不进行模糊标题合并。
结果保留来源链接、附件信息和可获得的原始提交状态。缺失提交状态表示未知。不同来源的结构化截止时间有冲突时,会保留这些时间并标记 deadlineConflict: true,不会自行选择一个日期。
公告和文档中的日期摘录单独放在 deadlineEvidence 中,始终标记 needsReview: true。这些摘录仅供人工核查,不会覆盖结构化截止时间,也不会推测日期格式、缺失年份、具体时间或作业归属。PDF 摘录带页码。
文档扫描覆盖内容文本页、标题或附件名与作业/考核/项目/测验/说明/课程大纲相关的内容,以及成绩簿关联页面,并非扫描所有课件的全部文字。
文档扫描默认开启;max_documents 默认 10,单次请求最多 30,单文档上限 10 MiB,PDF 最多处理 100 页。支持 PDF 文本、DOCX、PPTX、TXT 和 HTML;不提供 OCR,扫描版 PDF 需人工检查。请查看 documentScan、errors 和 truncated,返回零项不能证明没有待交作业。
登录与会话
Cookie 按以下顺序读取:
从 Chrome 自动读取有效的
BbRouter。自动读取失败时,使用环境变量
NTULEARN_COOKIE。前两项不可用时,尝试系统密钥环中的最近成功值。
浏览器读取成功后会尽力缓存到系统密钥环。遇到 HTTP 401 时,服务会清除失效缓存、重新读取会话并重试一次;若仍失败,请回到 Chrome 重新完成登录后再试。
平台注意事项
平台 | 注意事项 |
Linux | Chrome Cookie 解密可能需要解锁系统密钥环。MCP 客户端需保留 |
macOS | 首次访问可能弹出钥匙串授权窗口;确认进程来源后允许访问。 |
Windows | Chrome 的 App-Bound Encryption 可能阻止自动读取;可使用下面的手动配置。不建议为读取 Cookie 而提高整个 AI 客户端的权限。资料实际同步依赖 |
本仓库默认不自动读取 Edge、Firefox 或 Brave,部分上游配置文件中的多浏览器注释不适用于本版本。
手动配置 Cookie
自动读取不可用时,在已登录的 Chrome 中打开开发者工具,进入 Application → Cookies → ntulearn.ntu.edu.sg,复制 BbRouter 的 Value,在 MCP 配置对应服务的 env 中设置:
{
"NTULEARN_COOKIE": "expires:1234567890,id:REPLACE_WITH_YOUR_LOCAL_VALUE"
}这只是格式示例,请在本机填写真实值,不要提交到 GitHub、发送到聊天或分享给他人。会话过期后需要更新。该变量是备用来源;Chrome 自动读取成功时,以浏览器中的值为准。
可选配置
环境变量 | 默认值 | 用途 |
| 自动读取 | 手动会话备用值。 |
|
| Blackboard 实例地址;浏览器 Cookie 自动读取仍固定针对 NTULearn 域名。 |
|
| 普通附件下载未指定 |
这些变量可设置在 MCP 服务的 env 中。
常见问题
找不到 Cookie,或返回 401 / BbRouterExpiredError
先在 Chrome 中重新登录 NTULearn,完成 SSO / MFA,再重试。若仍失败,检查密钥环、桌面会话环境或手动配置。
客户端显示服务,但调用一直等待
检查是否有隐藏的钥匙串或密钥环授权窗口,也可在仓库目录运行 uv run ntulearn-mcp 查看启动错误。服务使用 stdio 协议,直接运行后等待输入属于正常现象。
大型 PDF 读取不完整,或提示超出容量
直接读取有文件、批量内容和图像输出限制。请用 ntulearn_download_file 下载后交给支持 PDF 的客户端读取;仅需少量图表时,可指定 mode: "vision" 和较小的 pages 范围,例如 "5"。
同步或作业汇总有遗漏
检查返回的错误、截断标记、文档扫描情况和文件状态。可以缩小课程范围、调整参数后重试;涉及截止时间时,应打开结果来源核实。
开发与测试
uv sync --frozen
uv run python -m unittest discover -s tests
uv run ntulearn-mcpsrc/ntulearn_mcp/
├── server.py # MCP 入口、工具处理、会话解析和文档读取
├── client.py # 异步 Blackboard REST 客户端
├── cookie.py # Chrome Cookie 自动读取
├── cache.py # 系统密钥环缓存
├── parsers.py # HTML 与下载链接解析
└── workflows.py # 资料增量同步和作业统一汇总测试使用 unittest,HTTP 请求通过 httpx.MockTransport 模拟。单元测试通过不代表真实账号、所有课程或所有平台均已验证。
隐私与使用说明
这是个人用途的非官方工具,与 NTU Singapore、Anthology 或 Blackboard 无隶属、赞助或背书关系。相关商标归各自所有者。
仅使用自己的账号,并遵守学校与课程的数据使用要求。
Cookie 在本机读取,用于向配置的 Blackboard 服务认证;不要公开真实 Cookie、
.env、成绩或私人课程资料。服务返回的课程、公告、成绩和文档内容会交由 MCP 客户端处理,可能随对话发送给模型提供方;请了解所用客户端的数据政策。
下载的课程资料不因本项目开源而获得再分发许可。
项目以 MIT 许可证 发布,按现状提供,不作担保。
致谢
本项目在 kingdonkatsu/ntulearn-mcp 的基础上开发,感谢原作者的工作。上游 MIT 许可证及版权声明见 LICENSE。
Available Tools
10 toolsntulearn_download_fileA
Download every file attached to a Blackboard content item to local disk. Handles both resource/x-bb-file (attachment API) and resource/x-bb-document (HTML body with bbcswebdav links) handler types. Pass destination_dir to target a specific folder — useful for organising a semester (e.g. destination_dir='~/NTU/y3s1/sc2002/week 8/'). Returns saved files with their resolved local paths and sizes. Use ntulearn_read_file_content if you want to inspect the content inline rather than saving to disk.
| Name | Required | Description | Default |
|---|---|---|---|
| course_id | Yes | Blackboard course ID (e.g. _12345_1) | |
| content_id | Yes | Content item ID (e.g. _67890_1) | |
| destination_dir | No | Optional target directory. Accepts absolute paths and `~`-prefixed paths (e.g. '~/NTU/y3s1/sc2002/week 8/'). Created if missing. Defaults to NTULEARN_DOWNLOAD_DIR env var, or ./downloads/ if unset. | |
| response_format | No | 'json' returns a structured payload (default, recommended for agents). 'markdown' returns a human-readable summary. | json |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| files | Yes | |
| title | No | |
| contentId | No | |
| destinationDir | No | |
| contentHandlerId | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, openWorldHint=true, idempotentHint=false and destructiveHint=false, so the safety profile is covered. The description adds practical side-effect context: it handles two Blackboard handler types, creates the destination directory if missing, and returns resolved local paths with sizes. It stops short of describing overwrite behavior or per-file failure handling, so it is strong but not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and keeps each sentence purposeful: handler coverage, destination_dir guidance, return summary, and sibling routing. It is slightly longer than strictly necessary because the destination_dir sentence largely repeats the schema description, but no sentence is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema already documents return structure and annotations cover the mutation/open-world profile, the description supplies the remaining decision context: handler types handled, destination behavior, and the inline alternative. It is nearly complete, with only minor gaps like overwrite semantics and error behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all four parameters are already documented in the input schema, including the destination_dir default and the response_format enum. The description restates destination_dir's purpose and example but adds no syntax or constraint detail beyond what the schema provides, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (download), resource (every file attached to a Blackboard content item) and destination (local disk). It also distinguishes the tool from ntulearn_read_file_content by naming the inline-inspection alternative, so an agent can tell them apart without opening both schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative sibling (ntulearn_read_file_content) and the condition that selects it ('inspect the content inline rather than saving to disk'). It also gives a concrete scenario for using destination_dir, leaving no ambiguity about when to use this tool versus its sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_get_announcementsARead-onlyIdempotent
Get announcements across your enrolled courses, newest first. By default fans out across every available course; pass course_ids=['_123_1'] to scope. Use since (ISO-8601) to filter to recent announcements only (e.g. "this week"). Each item includes the courseId it was posted to so cross-course views stay attributable. Supports pagination.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max items to return per call (1–200, default 50). Use small values to keep results within the LLM's context. | |
| since | No | Optional ISO-8601 cutoff (e.g. '2026-05-09T00:00:00Z'). Only announcements with `created` on/after this time are returned. Filtered client-side after fetch. | |
| offset | No | Number of items to skip for pagination. Use the nextOffset value returned by a previous call to walk pages. | |
| course_ids | No | Optional list of course IDs to scope to. Omit to fan out across all available enrolled courses. | |
| response_format | No | 'json' returns a structured payload (default, recommended for agents). 'markdown' returns a human-readable summary. | json |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| limit | Yes | |
| total | Yes | |
| offset | Yes | |
| hasMore | Yes | |
| nextOffset | No | |
| courseErrors | No | |
| announcements | Yes | |
| courseIdsQueried | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, openWorld, non-destructive), so the description's job is additive. It contributes ordering guarantees ('newest first'), the fan-out default, per-item courseId attribution for cross-course views, and pagination support — useful behavioral context beyond the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Multiple short sentences, front-loaded with purpose and default behavior before parameter guidance. Every sentence carries information, though the closing 'Supports pagination' is thin relative to the schema's own offset/nextOffset documentation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value explanation is not required. Given five all-optional parameters, full schema coverage, and annotations that carry the safety profile, the description supplies everything an agent needs: default scope, scoping mechanism, time filtering, attribution, and pagination.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the schema already documents limit, since, offset, course_ids, and response_format thoroughly. The description adds marginal value with a concrete course_ids example and a use-case framing for since, which nudges it above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Get'), an unambiguous resource ('announcements'), the scope ('across your enrolled courses'), and the ordering ('newest first'). The resource is clearly distinct from sibling tools like ntulearn_get_assignments, ntulearn_get_upcoming, and ntulearn_get_gradebook, so an agent can route without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete usage conditions: default fans out across all courses, pass course_ids to scope, and use since to narrow to recent items. It clearly describes when to apply each parameter but does not name an alternative tool or state exclusions, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_get_assignmentsBRead-onlyIdempotent
Aggregate gradebook, calendar and assignment pages with source links, exact-match merging, explicit submission status and conflicting deadlines. Includes separate unverified deadline excerpts from announcements and assignment documents; never treat excerpts as confirmed deadlines. Check errors/truncated/documentScan for coverage.
| Name | Required | Description | Default |
|---|---|---|---|
| max_items | No | ||
| course_ids | No | ||
| max_documents | No | ||
| include_documents | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, openWorld), and the description goes further by disclosing merging behavior (exact-match), conflict surfacing (conflicting deadlines), provenance (source links) and a trust caveat for unverified excerpts. That is meaningful behavioral context beyond the structured hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences, front-loaded with what is aggregated before the caveats, and no filler. The internal jargon ('exact-match merging', 'documentScan') is compact but assumes familiarity with the tool's return object.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully names coverage indicators (errors/truncated/documentScan) and provenance, but omits anything about batching across course_ids, result limits, or truncation behavior tied to max_items/max_documents. Adequate but with clear gaps for a 4-parameter aggregation tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and none of the four parameters (max_items, course_ids, max_documents, include_documents) are explained in prose. The mention of 'assignment documents' and documentScan implicitly gestures at the document-related flags, but limits, defaults and the meaning of course_ids are left entirely to the schema's bare types.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a concrete resource (assignments) and specifies the sources it reconciles: gradebook, calendar and assignment pages, plus unverified deadline excerpts. It is clear what the tool returns, but it never names a sibling (e.g. ntulearn_get_upcoming or ntulearn_get_gradebook) to differentiate itself from the tools that overlap most.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no stated condition for choosing this tool over ntulearn_get_upcoming or ntulearn_get_gradebook, even though the sibling set shows heavy overlap. The only guidance given ('never treat excerpts as confirmed deadlines') is about interpreting results, not about when to invoke the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_get_course_contentsARead-onlyIdempotent
Walk a course's content tree. Omit parent_id to get the top-level items (folders, documents, links, assignments); pass parent_id of a folder/lesson where hasChildren=true to drill into its children. Supports pagination.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max items to return per call (1–200, default 50). Use small values to keep results within the LLM's context. | |
| offset | No | Number of items to skip for pagination. Use the nextOffset value returned by a previous call to walk pages. | |
| course_id | Yes | Blackboard course ID (e.g. _12345_1) | |
| parent_id | No | Optional content item ID of a folder/lesson to list children of. Omit to list the course's top-level items. | |
| response_format | No | 'json' returns a structured payload (default, recommended for agents). 'markdown' returns a human-readable summary. | json |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| items | Yes | |
| limit | Yes | |
| total | Yes | |
| offset | Yes | |
| hasMore | Yes | |
| nextOffset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/openWorld/idempotent/non-destructive, so the safety profile is covered. The description adds genuinely useful behavior beyond that: the recursive tree-walk model, the hasChildren drill-down signal, and pagination support. It stops short of stating rate limits or page-size guidance.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the core action, then the two-mode rule, then pagination. No filler or restated boilerplate.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only traversal tool with an output schema (which carries return-shape detail) and full annotation coverage, the description supplies everything an agent needs: how to start at the root, how to descend, and that results are paginated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it explains the omit-vs-pass semantics of parent_id and the hasChildren precondition for valid drill-downs, which the schema alone only weakly conveys. It also reinforces the pagination intent of limit/offset.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Walk a course's content tree'. It immediately frames the tool as hierarchical traversal, which distinguishes it from siblings like ntulearn_search_course_content (search) and ntulearn_get_assignments (flat listing).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit conditions for the two modes: omit parent_id for top-level items, pass parent_id where hasChildren=true to drill in. However it never names an alternative tool (e.g. search_course_content) for when the agent already knows what it's looking for, so no exclusions are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_get_gradebookARead-onlyIdempotent
Get gradebook columns across your enrolled courses, including your scores where available. By default fans out across every available course; pass course_ids=['_123_1'] to scope. Each column carries the courseId it belongs to so cross-course views stay attributable. Supports pagination.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max items to return per call (1–200, default 50). Use small values to keep results within the LLM's context. | |
| offset | No | Number of items to skip for pagination. Use the nextOffset value returned by a previous call to walk pages. | |
| course_ids | No | Optional list of course IDs to scope to. Omit to fan out across all available enrolled courses. | |
| response_format | No | 'json' returns a structured payload (default, recommended for agents). 'markdown' returns a human-readable summary. | json |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| limit | Yes | |
| total | Yes | |
| offset | Yes | |
| columns | Yes | |
| hasMore | Yes | |
| nextOffset | No | |
| courseErrors | No | |
| gradeFetchError | No | |
| gradesAvailable | Yes | |
| courseIdsQueried | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, non-destructive, openWorld), so the bar is lower. The description still adds real behavioral context: the default fan-out scope, the per-column courseId attribution, and the fact that scores may be absent in some columns. It omits auth requirements and rate limits, but the added context is meaningful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, front-loaded with purpose and scope before secondary details (attribution, pagination). Every sentence carries information, though 'Supports pagination' is mildly redundant with the visible offset/limit parameters.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only, 4-parameter tool with full schema coverage, rich annotations, and an output schema, the description covers the essential call-time concerns: default scope, how to narrow, and attribution. It does not explain related-tool routing, but no sibling overlaps this resource, so the omission is minor.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 and the schema already explains limit, offset, course_ids, and response_format. The description goes slightly beyond by giving a concrete course_ids example ('_123_1') and confirming pagination behavior, adding modest value over the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Get'), resource ('gradebook columns'), and scope ('across your enrolled courses'), and the 'including your scores where available' clause tells the agent what data it yields. No sibling tool competes for this resource, so the distinction is unambiguous without needing to name another tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly describes the default behavior (fan out across every available course) and the condition for narrowing it (pass course_ids=['_123_1']), which is actionable guidance. It stops short of naming when-not-to-use or an alternative tool, so it lacks the exclusions that would make it a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_get_upcomingARead-onlyIdempotent
Get upcoming calendar items and assignment due dates across your enrolled courses. Wraps Blackboard's calendar API — assignment due dates surface as items with type='GradebookColumn'. By default returns the next 2 weeks across every available course (server fans out per-course in parallel). Pass course_ids to scope to specific courses, since/until (ISO-8601) to override the window, or type to filter (e.g. type='GradebookColumn' for due dates only).
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | Optional calendar item type filter. Use 'GradebookColumn' for assignment due dates only. | |
| limit | No | Max items to return per call (1–200, default 50). Use small values to keep results within the LLM's context. | |
| since | No | ISO-8601 start of the window (e.g. '2026-05-09T00:00:00Z'). Omit to default to now. | |
| until | No | ISO-8601 end of the window. Omit to default to two weeks after `since`. | |
| offset | No | Number of items to skip for pagination. Use the nextOffset value returned by a previous call to walk pages. | |
| course_ids | No | Optional list of course IDs to scope to. Omit to fan out across all available enrolled courses. | |
| response_format | No | 'json' returns a structured payload (default, recommended for agents). 'markdown' returns a human-readable summary. | json |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| items | Yes | |
| limit | Yes | |
| total | Yes | |
| offset | Yes | |
| hasMore | Yes | |
| nextOffset | No | |
| courseErrors | No | |
| courseIdsQueried | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, idempotentHint, destructiveHint=false, openWorldHint), so the bar is lower. The description still adds real behavioral context, notably that the server fans out per-course in parallel and the default window/scope, which the annotations do not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: identity first, then the type='GradebookColumn' mapping, then defaults and the three override levers. Nothing is padded and the most decision-relevant facts are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and pagination documented in the schema (offset/nextOffset), the description need not explain return values. It covers scope, window defaults, filtering, and fan-out behavior; only the routing distinction from sibling due-date tools is left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and each parameter already documents its role, defaults, and format, so the baseline is 3. The description restates the same guidance (course_ids scoping, since/until ISO-8601 override, type filter) plus one illustrative enum value, adding modest reinforcement rather than new meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get upcoming calendar items and assignment due dates across your enrolled courses') and immediately clarifies the relationship to a sibling by noting that due dates surface as type='GradebookColumn' rather than as a separate resource. An agent can separate this from ntulearn_get_assignments and ntulearn_get_gradebook without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operating context: default window is the next 2 weeks across all courses, and it names the three levers for changing that (course_ids, since/until, type). It stops short of stating when to prefer this tool over ntulearn_get_assignments or ntulearn_get_gradebook, which are the obvious alternatives for due-date questions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_list_coursesARead-onlyIdempotent
List courses the current user is enrolled in on NTULearn. By default returns only active/available courses. Set include_disabled=true to also include unavailable ones. Supports pagination via limit/offset.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max items to return per call (1–200, default 50). Use small values to keep results within the LLM's context. | |
| offset | No | Number of items to skip for pagination. Use the nextOffset value returned by a previous call to walk pages. | |
| response_format | No | 'json' returns a structured payload (default, recommended for agents). 'markdown' returns a human-readable summary. | json |
| include_disabled | No | Include courses where availability.available != 'Yes' |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| limit | Yes | |
| total | Yes | |
| offset | Yes | |
| courses | Yes | |
| hasMore | Yes | |
| nextOffset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, openWorldHint, and destructiveHint=false, so the safety profile is covered. The description adds a genuinely useful behavioral detail beyond annotations: the default result set silently excludes unavailable courses, which an agent must know to avoid misreporting a user's enrollments.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with what is listed and scoped, followed by the default behavior and the override, then pagination. No filler and every clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations covering safety, a full output schema, and 100% schema description coverage, the definition supplies the one thing structured data cannot: the default filtering semantics and the override flag. Return shape and pagination mechanics are adequately covered elsewhere, leaving only minor gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters are already fully documented in the schema, including limit bounds, offset/nextOffset usage, the response_format enum, and the include_disabled meaning. The description's mention of include_disabled and limit/offset restates what the schema already carries, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('List') plus resource ('courses the current user is enrolled in') and names the platform, giving the exact scope. This is clearly distinguishable from sibling tools that act on course contents, assignments, announcements, or gradebooks rather than the enrollment list itself.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The default-filter behavior ('returns only active/available courses') and the opt-in to include disabled ones imply when to use the flag, but no alternative tool is named and no explicit when/when-not guidance is given. Usage context is inferable rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_read_file_contentARead-onlyIdempotent
Read the content of files attached to a Blackboard content item, returned inline (no local-filesystem hop). Use this to ask questions about lecture material — ntulearn_download_file is for users who actually want the bytes on disk. PDFs default to text mode (via pypdf — cheap and almost always what you want for written content). Pass mode='vision' for diagram-, equation-, or screenshot-heavy pages; pair with pages='5' or pages='1-3' to keep the payload under MCP's 1 MB cap (~3K vision tokens per page). For multi-page diagram-heavy decks, prefer ntulearn_download_file plus drag-and-drop into claude.ai. Also supports Microsoft Office formats (.docx, .pptx with speaker notes, .xlsx with all sheets) and text-like files (txt, md, csv, json, xml, html with tags stripped, code files). Other binaries (images, video, audio, archives, legacy .doc/.ppt/.xls) are listed under skipped — fall back to ntulearn_download_file for those. Per-file cap 25 MB, batch cap 40 MB, vision cap 50 rendered pages; oversized files are skipped.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | PDF handling. 'text' (default) extracts text via pypdf — cheap, fits MCP's payload budget. 'vision' additionally renders each page as an image with PyMuPDF (~3K vision tokens per page); use for diagram/equation/handwritten content, ideally with a narrow `pages` range to stay under the 1 MB cap. Ignored for non-PDF files. | text |
| pages | No | Optional page range for PDFs (1-indexed, inclusive). Examples: '1-10', '3', '1,3,5', '1-5,8,10-12'. Omit to read all pages (vision mode capped at 50 rendered pages). Especially useful with mode='vision' to keep the response under MCP's 1 MB cap. | |
| course_id | Yes | Blackboard course ID (e.g. _12345_1) | |
| content_id | Yes | Content item ID (e.g. _67890_1) | |
| response_format | No | 'json' returns a structured payload (default, recommended for agents). 'markdown' returns a human-readable summary. | json |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| files | Yes | |
| title | No | |
| skipped | Yes | |
| contentId | No | |
| contentHandlerId | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, idempotentHint, non-destructive), but the description adds substantial behavior the annotations can't convey: the 1 MB MCP payload cap, ~3K vision tokens per page, per-file 25 MB / batch 40 MB / 50-rendered-page caps, and the fact that oversized or unsupported files land under `skipped` rather than failing. That is exactly the operational context an agent needs before invoking.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the sibling distinction before any parameter detail, then it moves to mode guidance, fallbacks, and caps. It is dense and nearly every sentence carries a distinct constraint, but at roughly 200 words it is long for a read tool and includes some redundancy with the schema's own mode/pages descriptions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value shape need not be explained, and annotations already carry the safety semantics. The description fills the remaining gaps a caller would hit — supported formats (Office variants with speaker notes/sheets, text-like files with HTML tag stripping), caps, the skipped-file behavior, and where to go when unsupported. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds decision rationale beyond the schema: it explains *why* text is the default ('cheap and almost always what you want for written content') and when vision is justified, and reinforces the mode+pages pairing. It is largely additive rather than duplicative, though a few points restate the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Read the content of files attached to a Blackboard content item') and immediately scopes the behavior ('returned inline (no local-filesystem hop)'). It also names the sibling it is not — 'ntulearn_download_file is for users who actually want the bytes on disk' — so an agent can distinguish the two without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use this ('ask questions about lecture material') vs the alternative ('users who actually want the bytes on disk'), then gives mode selection guidance (text default, vision for diagram/equation/screenshot-heavy), page-range pairing to stay under the 1 MB cap, and a fallback path for multi-page diagram decks. Even the unsupported-binary case is routed ('fall back to ntulearn_download_file for those').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_search_course_contentARead-onlyIdempotent
Recursively search a course's entire content tree for items matching a query. Matches on title or description (case-insensitive substring). Returns matched items with their full breadcrumb path.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search term (case-insensitive substring) | |
| course_id | Yes | Blackboard course ID (e.g. _12345_1) | |
| max_depth | No | Maximum recursion depth (default 5, capped at 10) | |
| max_results | No | Maximum number of matching items to return (default 50, capped at 200) | |
| response_format | No | 'json' returns a structured payload (default, recommended for agents). 'markdown' returns a human-readable summary. | json |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| matches | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld and non-destructive, so the safety profile is covered. The description adds the matching semantics (case-insensitive substring on title or description) and notes breadcrumb paths are returned, but that matching detail is essentially duplicated in the query parameter's schema description, leaving little net new behavior disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with zero filler: purpose first, matching rule second, return shape third. Nothing to trim and the most important information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present the description needn't explain return values, and annotations carry the safety profile; the description still supplies the matching rule and the recursive scope. It is nearly complete, only missing a pointer to the sibling it replaces for plain content listing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter (query, course_id, max_depth, max_results, response_format) is already documented in the schema with defaults and caps. The description adds no parameter-level detail beyond what the schema provides, which is the baseline expectation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (search) and resource (course content tree) with the recursive scope made explicit, plus what it matches on. An agent can distinguish it from a plain listing tool, but the description never names the nearest sibling (ntulearn_get_course_contents) that an agent would otherwise confuse it with.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: reach for this when you need to locate an item by name/text anywhere in a course rather than enumerate the tree. There is no explicit when-not guidance and no named alternative, so the agent must infer the boundary against ntulearn_get_course_contents on its own.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ntulearn_sync_materialsAIdempotent
Preview or incrementally sync course files into semester/course/original folders. Defaults to dry_run=true. Uses a manifest and checksums, preserves local edits, never deletes files. Inspect errors and truncated before claiming a complete sync.
| Name | Required | Description | Default |
|---|---|---|---|
| dry_run | No | ||
| semester | Yes | ||
| max_items | No | ||
| course_ids | No | ||
| verify_remote | No | Re-download to compare hashes even when modification timestamps match. | |
| destination_dir | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (idempotent, non-destructive, open-world), but the description adds real mechanism: manifest + checksum-based incremental logic, preservation of local edits, and the guarantee that files are never deleted. It still omits auth requirements, rate limits, and how the openWorld remote fetch behaves on failure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tight sentences, front-loaded with the action and mode, then defaults, then mechanism, then the validation caveat. No filler and every sentence carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, but the description compensates by naming the errors and truncated result fields an agent must check. Combined with the annotations, an agent has enough to invoke correctly, though the under-documented non-required parameters leave a small gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 17%, so the description must carry the load. It clarifies dry_run's default and hints at truncation (max_items) and the folder layout that informs semester/destination_dir, but course_ids and max_items semantics are never explained and semester/destination_dir formats remain unstated.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (sync) and resource (course files) with an explicit destination scope (semester/course/original folders), plus the two modes (preview vs incremental sync). This clearly separates it from every read-only sibling like ntulearn_download_file or ntulearn_get_course_contents.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operating context: default dry_run=true, use it to preview, and inspect errors/truncated before declaring success. It does not name alternatives or exclusions (e.g., when to prefer ntulearn_download_file), so it stops short of a full when/when-not statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.2.0- First observed
ntulearn_download_file - First observed
ntulearn_get_announcements - First observed
ntulearn_get_assignments - First observed
ntulearn_get_course_contents - First observed
ntulearn_get_gradebook - First observed
ntulearn_get_upcoming - First observed
ntulearn_list_courses - First observed
ntulearn_read_file_content - First observed
ntulearn_search_course_content - First observed
ntulearn_sync_materials
TDQS
Scored across 10 tools
Most tools target clearly distinct operations (list_courses, get_course_contents, search_course_content, download_file, read_file_content are unambiguous). However there is meaningful overlap between ntulearn_get_assignments (which aggregates gradebook+calendar+assignment pages), ntulearn_get_upcoming (calendar/due dates), and ntulearn_get_gradebook (gradebook columns) — an agent could reasonably pick the wrong one when asked about deadlines or grades, though the descriptions do clarify the sources each wraps.
Every tool uses the same ntulearn_<verb>_<noun> pattern (get_, list_, search_, sync_, download_, read_). Verb choices are consistent and predictable, with no mixing of styles or casing.
Ten tools is well-scoped for a student LMS integration: course listing, content walking/search, file access, announcements, gradebook, deadlines, and material sync each map to a real workflow without redundancy or filler.
The surface covers the read/lifecycle needs of an LMS client (courses, contents, search, download, read, announcements, grades, deadlines, sync) with no obvious dead ends. Minor gaps exist: no course-metadata/detail tool beyond listing, and no write operations beyond local file sync, but these are largely outside a read-oriented student client's scope.
Maintenance
Related MCP Connectors
Public read-only MCP access to digital learning concepts, semantic relationships, and citations.
Read-only access to Epivo's live course catalogue for AI agents.
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
Authenticated MCP access to the AIKI open knowledge commons.
Related MCP Servers
- FlicenseAqualityCmaintenanceEnables MCP-compatible clients to read courses, assignments, announcements, files, and grades from a Canvas LMS instance.10-
- AlicenseAqualityAmaintenanceLets MCP clients access a student's ZHAW Moodle account to list courses, find and download learning materials, sync local copies, and track deadlines, assignments, and announcements.16MIT
- AlicenseNot gradedqualityCmaintenanceEnables MCP-compatible AI clients to tutor students through a full learning workflow, including knowledge retrieval, assessments, mastery tracking, and personalized next-action recommendations.MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to browse your own university courses by downloading files, slides, announcements, deadlines, grades and lecture transcripts to your machine, then answering questions and searching across them through MCP tools. It logs in with your own account and refreshes only what changed.MIT