Canvas LMS集成服务器

一个提供Canvas LMS只读工具集成的本地模型上下文协议服务器,通过HTTP/SSE或标准输入输出提供课程、作业、公告等数据的结构化访问。

  • by XiyaoWang0519
  • TypeScript
  • 本地部署
  • 更新于
在 GitHub 查看 XiyaoWang0519/Canvas_MCP

使用说明

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_courses
    • list_assignments
    • get_assignment
    • list_announcements
    • list_upcoming
    • list_course_materials
    • resolve_external_downloads
    • list_user_files
    • list_course_files
    • list_folder_files
    • get_file
    • get_file_download_url
    • list_user_folders
    • list_course_folders
    • get_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客户端的共享密钥 /mcp
  • CANVAS_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,set 0 禁用)
  • MCP_MESSAGES_RATE_LIMIT_WINDOW_MS –速率限制窗口 /messages (ms,默认值 60000)
  • MCP_MESSAGES_RATE_LIMIT_MAX –最大值 /messages 每个窗口的请求数(默认值 120,set 0 禁用)
  • 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,set 0 禁用)

运行服务器

开发(手表模式)

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 –类型检查并发出JS
  • npm test –运行Vitest套件
  • 看 TESTING.md 查看完整的测试指南

后续步骤

  • 在有帮助的地方添加缓存或ETag支持
  • 一旦范围允许,实施写入操作(提交、评论)
  • 通过Docker进行容器化部署