使用说明
Canvas MCP服务器
本地模型上下文协议(MCP)服务器,通过HTTP/SSE(和可选的stdio)公开Canvas LMS只读工具,用于代理集成。服务器使用Canvas REST API,通过分页、重试和结构化JSON响应,提供对课程、作业、公告和即将完成的待办事项的身份验证访问。
特性
- HTTP+SSE传输
/mcp具有静态承载身份验证 - 用于本地测试的可选stdio传输(
--stdio) - Canvas客户端,具有:
- 自动承载或OAuth2令牌处理(401刷新)
- 429/5xx的指数回退抖动(最多3次尝试)
- 链接头分页(
per_page=100) - 可配置的请求超时(默认15秒)
- 结构化错误和日志记录的请求ID传播
- 返回强类型JSON有效载荷的工具:
list_courseslist_assignmentsget_assignmentlist_announcementslist_upcominglist_course_materialsresolve_external_downloadslist_user_fileslist_course_fileslist_folder_filesget_fileget_file_download_urllist_user_folderslist_course_foldersget_folder
- JSON控制台日志记录,包含请求元数据和Canvas状态代码
- Canvas 401/403/404/429/5xx响应的一致MCP错误映射
- 用于常见Canvas工作流的预构建MCP提示(快速入门、任务规划、公告、文件/材料发现)
入门指南
先决条件
- Node.js 18.17+或20+
安装
npm install
cp .env.example .env
# populate .env with Canvas credentials and MCP_BEARER
所需的环境变量(请参见 .env.example):
CANVAS_BASE_URL–例如。https://q.utoronto.ca- 身份验证:要么
CANVAS_PAT(个人访问令牌), 或- OAuth2:
CANVAS_CLIENT_ID,CANVAS_CLIENT_SECRET,CANVAS_REFRESH_TOKEN(+可选CANVAS_ACCESS_TOKEN)
MCP_BEARER–HTTP客户端的共享密钥/mcpCANVAS_TIMEZONE–(可选)用于时间戳转换的IANA时区名称(例如。America/Toronto,America/New_York).默认为UTC如果没有设置。CANVAS_HTTP_TIMEOUT_MS–(可选)Canvas HTTP调用的请求超时(毫秒)。默认为15000并且必须介于1000和120000.MCP_RATE_LIMIT_WINDOW_MS–速率限制窗口/mcp(ms,默认值60000)MCP_RATE_LIMIT_MAX–最大值/mcp每个窗口的请求数(默认值30,set0禁用)MCP_MESSAGES_RATE_LIMIT_WINDOW_MS–速率限制窗口/messages(ms,默认值60000)MCP_MESSAGES_RATE_LIMIT_MAX–最大值/messages每个窗口的请求数(默认值120,set0禁用)MCP_SESSION_TTL_MS–清理前的空闲会话TTL(ms,默认值900000)MCP_SESSION_CLEANUP_INTERVAL_MS–清理扫描间隔(ms,默认值60000)MCP_MAX_SESSIONS–并发SSE会话的可选上限(未设置为禁用)MCP_SSE_HEARTBEAT_MS–SSE保活间隔(ms,默认值25000,set0禁用)
运行服务器
开发(手表模式)
npm run dev
生产建设
npm run build
npm start
可选的stdio传输(适用于 claude-dev 或MCP兼容的CLIs):
npm run dev -- --stdio
# or
node dist/index.js --stdio
默认情况下,HTTP服务器侦听端口 3333.用覆盖 PORT=4000 npm run dev.
健康与认证
GET /healthz→{ "ok": true }- 全部
/mcp和/messages请求必须发送Authorization: Bearer <MCP_BEARER>
工具参考
每个工具通过以下方式返回JSON structuredContent (模式由Zod强制执行)。
课程和作业工具
| 工具 | 输入 | 输出 |
|---|---|---|
list_courses |
enrollment_state?: "active" | "completed", include_past?: boolean, limit?: number |
{ courses: Course[] } |
list_assignments |
course_id: number,可选 due_after, due_before (ISO 8601), search |
{ assignments: Assignment[] } |
get_assignment |
course_id: number, assignment_id: number |
{ assignment: Assignment } |
list_announcements |
可选 course_id,可选 since (ISO 8601) |
{ announcements: Announcement[] } |
list_upcoming |
可选 days (1-30,默认值7),可选 max_courses (1-100) |
{ upcoming: UpcomingItem[] } |
list_course_materials |
course_id: number,可选 include_types (File|Page|Assignment|Discussion|Quiz|ExternalUrl|ExternalTool),可选 include_html_link_extraction (默认值 true),可选 limit (1-1000,默认值 200) |
{ course_id, scanned_modules, scanned_items, materials: CourseMaterial[], truncated } |
resolve_external_downloads |
course_id: number,可选 material_keys: string[],可选 max_pages (1-100,默认值 20),可选 max_links_per_page (1-200,默认值 50),可选 timeout_ms (2000-60000,默认值 15000) |
{ course_id, processed_materials, results: ExternalResolutionResult[], total_links, truncated } |
文件和文件夹工具
| 工具 | 输入 | 输出 |
|---|---|---|
list_user_files |
可选 search_term, content_types (逗号分隔), sort, order |
{ files: File[] } |
list_course_files |
course_id: number,可选 search_term, content_types (逗号分隔), sort, order |
{ files: File[] } |
list_folder_files |
folder_id: number,可选 search_term, content_types (逗号分隔), sort, order |
{ files: File[] } |
get_file |
file_id: number |
{ file: File } |
get_file_download_url |
file_id: number,可选 submission_id |
{ file_id: number, download_url: string } |
list_user_folders |
(无) | { folders: Folder[] } |
list_course_folders |
course_id: number |
{ folders: Folder[] } |
get_folder |
folder_id: number |
{ folder: Folder } |
数据合约(稳定):
Course {
"id": 123,
"name": "ECE496",
"term": "Fall 2025",
"course_code": "ECE496H1"
}
Assignment {
"id": 456,
"course_id": 123,
"name": "Lab 2",
"due_at": "2025-10-05T23:59:00Z", // null when Canvas omits the value
"points": 10,
"html_url": "https://.../assignments/456",
"submission_state": "unsubmitted" // computed: unsubmitted | submitted | graded | pending_review | late | missing | excused
}
Announcement {
"id": 789,
"course_id": 123,
"title": "Midterm info",
"posted_at": "2025-10-01T14:30:00Z",
"html_url": "..."
}
UpcomingItem extends Assignment with { "source": "todo" | "assignment" }
File {
"id": 1234,
"uuid": "abc123...",
"folder_id": 567,
"display_name": "Lecture Notes.pdf",
"filename": "lecture_notes.pdf",
"content_type": "application/pdf",
"url": "https://.../files/1234/download",
"size": 1048576, // bytes
"created_at": "2025-09-15T10:00:00Z",
"updated_at": "2025-09-15T10:00:00Z",
"locked": false,
"hidden": false,
"locked_for_user": false,
"thumbnail_url": null,
"mime_class": "pdf"
}
Folder {
"id": 567,
"name": "Lectures",
"full_name": "course files/Lectures",
"context_id": 123,
"context_type": "Course",
"parent_folder_id": 100,
"created_at": "2025-09-01T00:00:00Z",
"updated_at": "2025-09-15T00:00:00Z",
"locked": false,
"folders_count": 3,
"files_count": 15,
"hidden": false,
"locked_for_user": false,
"for_submissions": false
}
list_upcoming 合并 /users/self/todo 以及请求范围内的即将到来的任务(桶过滤器),按任务id进行重复数据消除,并按最早的截止日期进行排序。使用 max_courses 限制扫描作业的课程数量。
list_course_materials 扫描课程模块+模块项目,消除重复资源,同时保留每个模块的放置参考,并在可用时用项目特定的数据丰富材料:
- 文件 项目包括文件元数据和临时
download_url - 页 项目包括页面元数据和轻量级正文片段
- 作业 项目包括到期/日期元数据和可选的从分配HTML中提取的API链接引用
- 外部URL/外部工具 项目包括已解析的目标URL
在手动浏览或基于浏览器的回退之前,将此工具用作课程内容发现的第一步。
resolve_external_downloads 是API-外部材料的第一个后续产品。它直接获取ExternalUrl目标,尝试对ExternalTool项进行Canvas无会话启动解析,提取候选可下载链接(绝对URL+置信度+API提示),并在可能需要浏览器回退时进行报告(needs_browser_fallback).它不执行页面JavaScript或向第三方系统进行身份验证。
文件下载
这 get_file_download_url 该工具返回一个临时的、签名的URL,允许直接下载文件内容。此URL:
- 有效期有限(通常为10分钟)
- 在URL中包含身份验证参数(不需要额外的标头)
- 直接指向Canvas的文件存储(通常是AWS S3)
- 不应长期缓存或存储
当您需要访问实际文件内容时,请使用此工具。仅适用于文件元数据(名称、大小、类型等),请使用 get_file 相反。
快速参考
| 提示 | 输入 | 目的 |
|---|---|---|
canvas.quickstart |
(无) | 启动说明,提醒模型如何使用可用工具安全地探索Canvas数据,包括模块材料和文件访问发现。 |
canvas.assignment_brief |
course_hint?: string, days?: string (digits) |
通过为特定课程和时间范围收集作业和即将进行的待办事项来指导模型。 |
canvas.announcement_digest |
course_hint?: string, since?: string (ISO-8601) |
帮助模型编译最近公告的摘要,可以选择将范围限定在课程或时间范围内。 |
canvas.file_access |
course_hint?: string, file_type?: string |
引导模型定位、浏览和访问Canvas文件。包含 list_course_materials-第一次发现、文件夹导航、内容类型过滤和下载URL使用。 |
日志记录和错误
日志是写入stdout/stderr的JSON文档,带有字段: tool, status, duration_ms, canvas_status, req_id,可选 extra_req_ids & error 总结。秘密永远不会被记录。
Canvas错误映射到MCP错误,并显示面向用户的消息:
| 画布状态 | 消息 |
|---|---|
| 401/403 | Authorization failed: check Canvas token/scopes. |
| 404 | Not found: course or assignment id. |
| 429 | Rate limited by Canvas; retry later. |
| 5xx | Canvas temporarily unavailable. (向客户端显示为HTTP 503) |
每个错误包括 request_id (画布 X-Request-Id)MCP错误中的最后一个Canvas状态代码 data.
测试
npm run build–类型检查并发出JSnpm test–运行Vitest套件- 看
TESTING.md查看完整的测试指南
后续步骤
- 在有帮助的地方添加缓存或ETag支持
- 一旦范围允许,实施写入操作(提交、评论)
- 通过Docker进行容器化部署