canvas-parent-mcp
MCP server for Canvas LMS (Instructure) — read courses, grades, assignments, announcements, planner items, and conversations; download course files. Mirrors the parent/observer scope of the sibling infinitecampus-mcp.
Setup
Option A — npx (recommended)
Add to .mcp.json in your project or ~/.claude/mcp.json:
{
"mcpServers": {
"canvas": {
"command": "npx",
"args": ["-y", "canvas-parent-mcp"],
"env": {
"CANVAS_BASE_URL": "https://cms.instructure.com"
}
}
}
}
With the ContextMint Bridge browser extension installed and a signed-in Canvas tab, that's enough — the MCP reads your session cookies from the browser on the first request and again whenever the session expires, so keep the extension installed and the browser running. Add CANVAS_TOKEN, CANVAS_CLIENT_*/CANVAS_REFRESH_TOKEN, or CANVAS_USERNAME/CANVAS_PASSWORD to the env block if you'd rather use one of those modes.
Option B — from source
git clone https://github.com/chrischall/canvas-parent-mcp
cd canvas-parent-mcp
npm install && npm run build
Authentication
fetchproxy fallback (recommended, zero-config). Set only CANVAS_BASE_URL. Install the ContextMint Bridge browser extension, sign into your Canvas instance once. The MCP reads canvas_session + pseudonym_credentials cookies from your tab on the first request and re-reads them on every 401; API calls go directly from Node in between, but the extension must stay reachable to renew the session. Works with any auth flow (SSO/SAML/2FA included). It's the fetchproxy extension under its new name, same maintainer (fetchproxy's README points to it); verify a release zip with shasum -a 256 -c <zip>.sha256 or build it from source.
Alternatives (env-var)
- Personal access token — set
CANVAS_TOKEN. Most institutions have disabled this for non-admins. - OAuth — set
CANVAS_CLIENT_ID,CANVAS_CLIENT_SECRET,CANVAS_REFRESH_TOKEN. Bootstrap viacanvas-parent-mcp-qr-login. - Username/password (session-scrape) — set
CANVAS_USERNAME+CANVAS_PASSWORD. Direct Canvas accounts only (no SSO/2FA). Brittle.
Precedence when multiple are set: CANVAS_TOKEN > username/password > OAuth > fetchproxy. Set CANVAS_DISABLE_FETCHPROXY=1 to opt out of the fallback.
Tools (prefix canvas_)
Profile & observees
canvas_get_profile— your Canvas profilecanvas_healthcheck— which auth path resolved and whether Canvas accepts it; registered even when auth is unconfigured, so it can say whycanvas_list_observees— students linked to your observer account
Courses
canvas_list_courses— your active courses with gradescanvas_get_course(courseId, view?)— course detail with syllabus + teachers
Assignments & submissions
canvas_list_assignments(courseId)— assignments in a coursecanvas_list_missing_submissions— past-due unsubmitted workcanvas_get_submission(courseId, assignmentId, view?)— your submission with comments + rubriccanvas_list_recent_submissions(courseId, view?)— recently graded submissions (default 14d)
Grades
canvas_list_enrollments— per-course grades
Calendar & planner
canvas_list_calendar_events— calendar events / assignmentscanvas_list_upcoming_events— server-curated next 7 dayscanvas_list_planner_items— unified to-do feed
Communication
canvas_list_announcements(contextCodes, view?)— course announcementscanvas_list_conversations— inboxcanvas_get_conversation(id, view?)— full conversation threadcanvas_list_discussion_topics(courseId, view?)— course discussion topics
Files
canvas_list_course_files(courseId)— file metadatacanvas_download_file(url, destinationPath)— download a file to disk.urlmust be a/files/URL on the configured Canvas host;destinationPathmust be insideCANVAS_OUTPUT_DIR(default~/Downloads), and a relative path is resolved against it
Response shape (view)
Nine tools take view: "compact" | "full", and compact is the default —
you get the slim shape without asking for it:
canvas_get_profile, canvas_list_observees, canvas_get_course,
canvas_get_submission, canvas_list_recent_submissions,
canvas_list_announcements, canvas_list_conversations,
canvas_get_conversation, canvas_list_discussion_topics.
Compact here is media stripping, not a field projection — do not expect a
field list. It removes the avatar URLs Canvas hangs on user objects
(avatar_url, avatar_image_url) and nothing else. This server hands back
Canvas's payload verbatim and holds no verified record of which of Canvas's
fields a caller needs, so it does not claim to keep some and drop others: a
record that came back with holes in it would read exactly like a verified
answer. Stripping is subtractive, so it cannot lose a field nobody knew about.
A conversation participant keeps their name and id on compact; they lose their
picture.
- The link fields survive both rungs, named explicitly.
url,html_urlandpreview_urlare kept by name —html_urlopens the item in Canvas,preview_urlrenders a submission, andurlis the download handlecanvas_list_course_filesemits andcanvas_download_fileconsumes. On anonline_urlsubmission thaturlIS the student's submitted work. Without the name, a student who submitted a link ending in.pngwould have had their submission silently removed on the default rung. - Both avatar spellings really do appear. One live
canvas_list_conversationsresponse carried a default avatar (…/avatar-50.png) on one participant and an uploaded, extension-less thumbnail id on another. They are dropped by NAME, so both go — not one kept and one lost depending on whether that person ever uploaded a photo.
Pass view: "full" when you want Canvas's payload untouched, avatars and all.
That is also why there is deliberately no raw rung: there is no
normalisation layer here for full to sit above, so full already IS the
untouched upstream payload and a third value would silently alias it.
The other ten tools take no view, for two different reasons:
- Nothing to strip.
canvas_list_courses,canvas_list_assignments,canvas_list_missing_submissions,canvas_list_enrollments,canvas_list_calendar_events,canvas_list_upcoming_eventsandcanvas_list_planner_itemsembed no user object — a livecanvas_list_coursesover twelve courses carried no media field of any kind — and a knob that does not turn is worse than no knob.canvas_healthcheckreturns a verdict, not a record. - The URL is the product.
canvas_list_course_filesandcanvas_download_fileexist to hand you a file URL. Compact there would not shrink the response, it would empty it.
Passing view to one of those ten is not an error and not a warning: MCP tool
schemas are non-strict, so the key is dropped and you get that tool's ordinary
output.
Notes
- Set
CANVAS_NAMEif you want a friendly label other than the host portion of the base URL. - All read tools that target a user accept an optional
observeeIdparameter (defaults toself) — useful when an observer is checking on a linked student.
