codex_canvas_mcp
A local MCP server for reading Canvas and, only after multi-gate safety checks, authoring specific course content.
Read (available by default): current user profile, course modules/items, and any Canvas API v1 GET path — plus rubric inspection and write-policy status.
Create/update content (disabled by default): pages, modules and module items, assignments (and description-only updates), discussions, announcements (immediate or scheduled, section audiences, 72-hour display windows), and Classic Quizzes with questions.
Rubrics: create/attach from a Canvas assignment URL; delete only course-owned rubrics with zero usage.
Syllabus: preview, replace, and verify body-only changes with a hash check.
Uploads: one signature-verified image (PNG/JPEG/WebP ≤10 MiB) from a trusted local folder, or one validated macro-free PPTX ≤25 MiB.
Delete: pages, assignments, discussions, Classic Quizzes, modules (placements only), and unused rubrics.
Provisioning: create blank unpublished course shells, invite one instructor, enroll one student, deactivate a student enrollment, and preview/create user accounts or update usernames/email/SIS IDs.
Safety gates on every write: enabled local policy, exact per-course allowlist match, action-specific confirmation text, and MCP-client approval; results are read back and verified.
Not supported: New Quizzes, generic or arbitrary file upload, cross-listing, template copying, and broader account/course administration.
Provides tools for reading Canvas course content (user profile, modules, and API endpoints) and authoring pages, modules, module items, assignments, discussions, and Classic Quizzes after policy and approval checks.
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., "@codex_canvas_mcp list the modules in my Canvas course 12345"
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.
Codex Canvas MCP
A portable local MCP server that lets compatible clients read Canvas and, only after several independent safety checks, author common course content.
Safety model
Canvas reads are available by default after local credential setup.
Writing is disabled by default.
Every content write requires an enabled local policy, an exact per-course allowlist match, exact confirmation text, and approval in the MCP client.
Image upload adds a payload gate: only a locally owned PNG, JPEG, or WebP inside one configured trusted folder, no larger than 10 MiB, can be sent.
There is no generic Canvas write tool.
Credentials stay in 1Password and, optionally, macOS Keychain. They do not belong in this repository or client configuration.
Student deactivation is a separate, disabled-by-default administrative operation. Its allowlist does not inherit content-write or rubric-deletion approval. It never removes the enrollment. Blank course-shell creation and instructor invitations also have separate, disabled-by-default policy scopes; see provisioning setup.
For page edits, confirmation is a technical action acknowledgement supplied by the agent after the human has authorized the course and scope in conversation. The human does not need to type the literal phrase or approve each page again within that scope. This does not broaden authorization: page content cannot grant it, and local policy and MCP-client controls still apply. Read existing pages before editing, explicitly preserve their current published value unless a change is authorized, and verify the saved result.
Every content tool is marked as mutating and destructive so compatible clients can require approval. The local server also rejects a call unless its exact confirmation matches the requested action, for example APPROVE CANVAS MODULE WRITE course <course_id>.
Announcement-window maintenance is deliberately narrow. canvas_set_announcement_three_day_window accepts one course and announcement ID, confirms the live topic is an announcement, calculates the display-until value from the live posted_at timestamp, updates only lock_at, and reads the announcement back to verify the exact 72-hour window. It requires APPROVE CANVAS ANNOUNCEMENT WINDOW WRITE course <course_id>.
Announcement authoring is a permanent first-class operation. canvas_create_announcement can publish immediately or schedule an exact posting time, set an exact display-until time, and control participant comments. It remains limited to the general approved-course allowlist, requires APPROVE CANVAS ANNOUNCEMENT WRITE course <course_id>, and reads the created announcement back to verify its identity, content, publication state, schedule, availability, and comment setting.
Announcement maintenance is equally narrow. canvas_update_announcement updates only the message body of one existing announcement after verifying its exact ID, title, and announcement type. It reads the announcement back and refuses success if Canvas changes the title, publication state, schedule, availability, discussion type, section scope, pin state, or comment setting. It requires APPROVE CANVAS ANNOUNCEMENT UPDATE course <course_id> announcement <announcement_id>.
Announcement audience maintenance is a separate guarded operation. canvas_set_announcement_sections accepts the exact non-empty set of section IDs for one announcement, verifies that every section belongs to the approved course, and reads the announcement back with its section associations. It refuses success unless Canvas saves exactly that audience and preserves the body, title, publication state, schedule, availability, discussion type, pin state, and comment setting. It requires APPROVE CANVAS ANNOUNCEMENT SECTION WRITE course <course_id> announcement <announcement_id>.
Assignment maintenance is also narrowly scoped. canvas_update_assignment updates only the description of one existing assignment after verifying its exact ID and name. It reads the assignment back, confirms the description after Canvas's known link-attribute normalization, and refuses success if Canvas changes protected settings such as points, submission type, dates, group, or publication state. It requires APPROVE CANVAS ASSIGNMENT UPDATE course <course_id> assignment <assignment_id>.
Rubric maintenance uses a separate operation-specific course allowlist and a stronger identity gate. canvas_delete_rubric requires APPROVE CANVAS RUBRIC DELETE course <course_id> rubric <rubric_id>, an exact expected title, course ownership, editable state, and an empty live used_locations result. Authorizing rubric deletion does not authorize any page, assignment, module, discussion, or quiz write. The tool returns the complete pre-deletion rubric definition and verifies that the rubric no longer appears in the active course list.
Rubric creation accepts a same-origin Canvas assignment URL, an assignment module-item URL, or a course URL containing an assignment_id query parameter. Query strings and fragments are ignored after the course and assignment are resolved. The URL never bypasses course policy: its course ID must still be explicitly allowlisted, and rubric creation requires APPROVE CANVAS RUBRIC WRITE course <course_id>. The tool refuses to replace an existing assignment rubric, requires descriptions for every criterion and rating, and requires grading-rubric points to match the assignment points.
Related MCP server: canvas-lms
What is included
Tool | Capability | Default |
| Read the authenticated profile | Available |
| Read modules and items for one course | Available |
| GET a normalized Canvas API v1 path | Available |
| Read one rubric, its complete definition, usage locations, and deletion preflight | Available |
| Inspect local policy state | Available |
| Preview a blank shell and check its account, term, and duplicates | Available |
| Create and verify a private unpublished shell | Blocked |
| Verify an existing instructor identity, section, and role | Available |
| Invite and verify one instructor with an approved teacher role | Blocked |
| Verify one student's identity, course, section, role, and existing enrollment | Available |
| Invite and verify one exact student after an explicit request | Blocked |
| Read one exact student enrollment and preview deactivation | Available |
| Deactivate and verify one active student enrollment | Blocked |
| Upload one validated macro-free PPTX from its trusted folder | Blocked |
| Upload one verified image from the configured trusted folder | Blocked |
| Create or update one page | Blocked |
| Preview, replace, and verify only a course syllabus body | Blocked |
| Create one module | Blocked |
| Place one content item in a module | Blocked |
| Create one assignment | Blocked |
| Update and verify one assignment description | Blocked |
| Create and attach one assignment rubric from a Canvas URL | Blocked |
| Create one discussion | Blocked |
| Create and verify one immediate or scheduled announcement | Blocked |
| Update and verify one exact announcement body | Blocked |
| Set and verify one announcement's exact section audience | Blocked |
| Set one announcement to a verified 72-hour display window | Blocked |
| Create one Classic Quiz | Blocked |
| Add one question to a Classic Quiz | Blocked |
| Delete one page | Blocked |
| Delete one assignment | Blocked |
| Delete one discussion | Blocked |
| Delete one Classic Quiz | Blocked |
| Delete one module and its item placements | Blocked |
| Delete one exact, course-owned rubric only when Canvas reports zero usage locations | Blocked |
Set up a new Mac
Prerequisites: Python 3.11 or newer, the 1Password CLI, a 1Password service account that can read only the intended Canvas credential item, and a Canvas API token with the least privileges practical for your work.
Clone and install into an isolated environment:
git clone https://github.com/mrchris-ai/codex_canvas_mcp.git cd codex_canvas_mcp python3 -m venv .venv . .venv/bin/activate python -m pip install --upgrade pip python -m pip install -e .Create a 1Password item whose concealed field contains the Canvas API token. Give the service account read access only to the containing vault/item. Record the vault name, item name or ID, and concealed field label; do not put their values in this repository.
Choose one service-account-token delivery method:
For a short-lived shell session, provide
OP_SERVICE_ACCOUNT_TOKENto the MCP server process through your local secret launcher.On macOS, store that token in Keychain and configure non-secret lookup labels. Example command (it prompts securely; do not put the token on the command line):
security add-generic-password -U -s "your-service-label" -a "your-account-label" -w
Configure the server process with these non-secret values:
Variable
Purpose
CANVAS_BASE_URLCanvas HTTPS origin, such as
https://school.instructure.comCANVAS_OP_VAULT1Password vault name or ID
CANVAS_OP_ITEMCanvas credential item name or ID
CANVAS_OP_FIELDConcealed token field label; defaults to
credentialCANVAS_KEYCHAIN_SERVICEmacOS Keychain service label when not using
OP_SERVICE_ACCOUNT_TOKENCANVAS_KEYCHAIN_ACCOUNTmacOS Keychain account label when not using
OP_SERVICE_ACCOUNT_TOKENCANVAS_IMAGE_UPLOAD_ROOTAbsolute path to the only local folder from which image upload is allowed
Test only the protocol and security gates, without contacting Canvas or reading credentials:
python -m unittest discover -s tests -v printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python -m canvas_mcpAdd the stdio server to Codex. The project-level form is:
[mcp_servers.canvas] command = "/absolute/path/to/codex_canvas_mcp/.venv/bin/python" args = ["-m", "canvas_mcp"] [mcp_servers.canvas.env] CANVAS_BASE_URL = "https://school.instructure.com" CANVAS_OP_VAULT = "your-vault" CANVAS_OP_ITEM = "your-item" CANVAS_OP_FIELD = "credential" CANVAS_KEYCHAIN_SERVICE = "your-service-label" CANVAS_KEYCHAIN_ACCOUNT = "your-account-label" CANVAS_IMAGE_UPLOAD_ROOT = "/absolute/path/to/reviewed/canvas-images"Put this in the trusted project's
.codex/config.tomlor your personal Codex configuration. Do not add secret values. Restart or open a new task after changing MCP configuration, then verify the listed tools before requesting Canvas data.
ChatGPT compatibility
This repository implements a local stdio MCP server for Codex and other clients that support local stdio MCP, including desktop Work configurations that provide a local executor. A hosted ChatGPT custom app requires a supported private tunnel or a reviewed remote MCP endpoint. Do not expose this process directly to the public internet.
See Developer mode and MCP apps in ChatGPT before attempting ChatGPT setup; availability and approval behavior can change.
Clients that omit configured environment values
Some desktop Work launch paths transfer the names from an MCP server's env table as env_vars to inherit, without transferring the values. The server can therefore report CANVAS_BASE_URL missing even when the saved MCP configuration contains it. A separate terminal or Codex session succeeding does not verify that Work's active MCP has the same configuration.
For this launch path, explicitly provide a private connection JSON file:
{
"CANVAS_BASE_URL": "https://school.instructure.com",
"CANVAS_OP_VAULT": "your-vault",
"CANVAS_OP_ITEM": "your-item",
"CANVAS_OP_FIELD": "credential",
"CANVAS_KEYCHAIN_SERVICE": "your-service-label",
"CANVAS_KEYCHAIN_ACCOUNT": "your-account-label"
}Keep this file outside the repository. It must be a regular, non-symlink file owned by the current user, with permissions 0600, and no larger than 16 KiB. Set those permissions before adding your lookup references. It contains connection metadata only: never put a Canvas token or a 1Password service-account token in it. Credentials still come from the existing Keychain/1Password lookup flow. The file option requires a platform supporting ownership and no-follow file checks, such as macOS or Linux.
Add the absolute file path to the saved MCP arguments:
[mcp_servers.canvas]
command = "/absolute/path/to/codex_canvas_mcp/.venv/bin/python"
args = ["-m", "canvas_mcp", "--connection-config", "/absolute/private/path/canvas-connection.json"]The loader fills missing connection metadata. Matching environment settings are preserved; conflicting values, invalid origins, duplicate keys, and unknown fields fail startup without echoing configuration values. CANVAS_BASE_URL is required in the file. The only other accepted keys are the five lookup settings in the example.
The file cannot set write policy, upload roots, arbitrary environment variables, or credentials. Preserve any existing MCP environment configuration for clients that support it. A client with no separately provisioned write policy remains unable to write; an existing independently configured policy is unchanged. The credential helper executables must still be available on the host's PATH.
For existing clients that cache the old command arguments, the server also checks ~/.config/canvas-mcp/connection.json when no explicit file argument is supplied and CANVAS_BASE_URL is missing or empty. This optional default uses the same strict private-file validation. A missing default leaves the original environment-only behavior unchanged; an unsafe default fails startup. An explicitly specified file never falls back, and a client with an existing base URL does not consult the default.
After changing the arguments, reload the connector in the affected client and verify a real read through its active MCP. Also inspect canvas_get_write_policy to confirm the connection repair has not changed write access.
Enabling content-authoring tools
Keep writing off unless a specific task requires it.
Copy
config/write-policy.example.jsonto a location outside the repository.Add only the exact Canvas course IDs approved for content authoring to
approved_course_ids. Add courses approved only for zero-dependency rubric deletion toapproved_rubric_delete_course_ids, and courses approved for student deactivation toapproved_student_deactivation_course_ids. Each list is independent.Set
enabledtotrueand restrict the file:chmod 600 /absolute/private/path/write-policy.jsonAdd
CANVAS_WRITE_POLICY=/absolute/private/path/write-policy.jsonto the MCP server environment.Restart the client if the environment variable was added or changed, then call
canvas_get_write_policyto verify the effective policy. Edits to an already configured policy file are read on each guarded operation and do not require a restart.For each content write, review the exact target and payload and honor MCP-client approval controls. For page edits already authorized by the human, the agent supplies the action-specific confirmation field; do not ask the human to repeat a literal phrase for each page or batch.
Disable the policy again when the task is complete.
Work clients that omit the write-policy environment value
Version 0.18.2 also checks ~/.config/canvas-mcp/write-policy.json when CANVAS_WRITE_POLICY is absent or blank. This is a separate, explicitly provisioned write policy, not connection metadata. Nothing creates or enables it automatically. Use the same policy schema and exact task-authorized grants described above; keep private policy files outside Git.
An explicit CANVAS_WRITE_POLICY path always takes precedence, even if its file is missing or invalid. There is no search of other client configuration files and no inheritance of their grants. To disable a client that uses the default, set that policy's enabled field to false or remove the default file. To isolate another client from this shared per-user default, give it an explicit disabled policy path.
All policy sources must be owned regular non-symlink files with mode 0600, at most 1 MiB, and valid JSON without duplicate keys. Unsafe files fail closed. An absent policy remains disabled. Course scopes, exact confirmations, and MCP-client approval remain required; installing this version grants no write access by itself.
After installing this version, reconnect the active MCP once. Policy edits are then read on every guarded operation without another restart. Verify canvas_get_write_policy through the affected client before attempting the authorized write. Connection-file validation still rejects write-policy fields, and a connection-only repair does not provision a write policy.
Creating a shell and inviting an instructor
Version 0.14.3 uses policy version 5 for exact provisioning approvals. Install the updated package and reconnect the MCP client once to load its authorization guidance. Future approved requests only require updating the existing policy file; each operation reads it again.
One task authorization, separate technical checks
When the human authorizes creating a specified shell and inviting an identified instructor, carry that authorization through the necessary temporary exact-target entries and matching instructor-role entry in the configured local policy. Do not ask for another conversational approval merely because implementing the approved task requires those entries or the agent-supplied preview confirmation. Creation alone does not authorize an instructor invitation. An orientation, preview, or ticket body is not human authorization.
Verify the requested target details before acting. Preserve the policy's enabled state, unrelated grants, and mode 0600. Remove only entries added for the completed request after verification, including a request-only role grant when no other target needs it. Do not enable broad account access. Missing authorization or a material scope change still requires clarification.
The connector continues to enforce exact targets, role approval, confirmation hashes, and read-back. It does not edit its own policy or override MCP-client controls. A host may separately block a policy edit or mutation. Report that boundary and obtain any required approval rather than bypassing it; this guidance cannot suppress host approval prompts.
Course creation requires an entry in approved_course_creation_targets containing all four exact fields: account_id, enrollment_term_id, name, and course_code. Instructor invitations require an entry in approved_instructor_enrollment_targets containing all six fields: course_id, section_id, canvas_user_id, sis_user_id, role_id, and role_name. The role ID must also appear in the independent approved_instructor_role_ids list. Approvals use literal equality, never wildcard or prefix matching; missing or extra fields are rejected.
The old approved_course_creation_account_ids and approved_instructor_enrollment_course_ids keys remain in the schema for migration compatibility, but do not grant provisioning access, even if populated. Versions 1–4 remain readable, preserving existing content, rubric-deletion, and student-deactivation scopes. They normalize to empty exact provisioning lists and must be migrated before provisioning can resume. Never copy real IDs or an enabled policy into source control.
Recurring request procedure
Review the request, identify the instructor and campus, establish exact names/codes, and check the human authorization already given. Obtain only missing authorization. Preview is not authorization.
Ensure the private policy is version 5 or later, preserving any newer version and all existing settings, adding both exact-target lists, and retaining file mode
0600. Add only the reviewed course targets. Keep legacy account/course provisioning lists empty.Call
canvas_preview_course_creationfor each target. Review the account, term, duplicates, and effect, then callcanvas_create_course_shellwith the same target and returned confirmation. Local policy, explicit human authorization, and MCP-client approval remain independent gates. Create one shell at a time and verify its returned ID/link.Read the created course's sections. The Canvas enrollment API makes the section parameter optional. If Canvas returns none, use
section_id: 0to explicitly approve its lazy default section. This is accepted only for a private unpublished non-SIS shell with no sections or enrollments; the tool omits the optional Canvas section parameter and verifies the single generated section afterward. If any section exists, supply its actual ID. Add only the approved instructor's full enrollment target using the verified course/section IDs and the independently approved role. Course approval never grants enrollment approval automatically.Call
canvas_preview_instructor_enrollment, thencanvas_enroll_instructorwith that exact target and its returned confirmation. The instructor is invited and Canvas notification is requested; delivery is not independently confirmed.Verify the enrollment and course link. Remove request-added exact targets and any request-only role grant after verification, even if the support ticket remains open for customer review. The installed schema version stays in place for subsequent requests; no broad subaccount enablement is needed.
Provisioning list reads follow Canvas pagination links instead of guessing numbered pages. Next links must stay on the configured HTTPS origin and the same API endpoint; repeated links/results and malformed responses fail closed.
A changed name, code, term, campus, instructor, section, or role requires its own reviewed exact target. Fresh confirmation text cannot expand policy scope. An uncertain write must be inspected before any retry; never automatically retry or delete its result.
Deactivating one student enrollment
First confirm the official enrollment disposition outside Canvas. Use canvas_preview_student_deactivation with the course ID, section ID, enrollment ID, Canvas user ID, and SIS user ID. It shows the live state and makes no change. The mutation accepts the same exact IDs plus APPROVE CANVAS STUDENT DEACTIVATION course <course_id> enrollment <enrollment_id> after human authorization and MCP-client approval. The local policy must be version 3 or later and the course must appear in approved_student_deactivation_course_ids; version 1 and 2 policies remain readable but grant no deactivation access. The Canvas credential also needs permission to view SIS IDs and manage course student enrollments; use a token limited to the needed API scopes where supported.
The tool refuses a mismatched identity or a non-active enrollment, sends only Canvas's task=deactivate action to the exact enrollment, and confirms the resulting inactive state through a second read. An already inactive enrollment returns a verified no-change result. If verification fails after the request, inspect the Canvas roster before retrying. Canvas's Enrollments API documents deactivation as distinct from deletion; restoring access requires a separately authorized reactivation action, which this connector does not provide.
Authoring in a Blueprint course
A Blueprint course uses the same exact-course content allowlist as any other course. Approving an associated course does not approve its Blueprint, and approving a Blueprint does not approve its associated courses. Keep real course IDs in the private policy file, never in the repository or example configuration.
Before editing, use canvas_read_api to verify the target course's blueprint flag and, when needed, its associated courses at /api/v1/courses/<course_id>/blueprint_templates/default/associated_courses. Add the explicitly authorized Blueprint ID to the existing private approved_course_ids list, preserving the other entries and file mode 0600. Verify the effective list with canvas_get_write_policy.
Preserve the original content, use the resource-specific write tool and exact confirmation phrase, and read the saved resource back through the MCP. Publishing or updating a page in the Blueprint is separate from syncing it to associated courses. This connector does not provide Blueprint synchronization.
The course syllabus is not a Canvas Page. Inspect it with canvas_read_api at /api/v1/courses/<course_id> using include[]=syllabus_body, then use canvas_update_syllabus for body-only changes. The endpoint is documented in the Canvas Courses API.
The syllabus tool requires the exact course allowlist, APPROVE CANVAS SYLLABUS WRITE course <course_id>, client approval, and expected_sha256 calculated from the reviewed current UTF-8 syllabus body. It defaults to dry_run: true, returning the original body for recovery. Save that recovery content before explicitly setting dry_run: false. The only outgoing field is course[syllabus_body]; extra arguments and empty bodies are rejected. It reads the course back and verifies exact body equality and protected course settings. If Canvas normalizes the HTML or a setting differs, the tool reports a verification failure after the write; inspect live state before retrying. The hash check detects changes before the PUT but is not an atomic Canvas concurrency lock.
To restore a saved syllabus, review the live version, compute its current hash, and submit the saved body through the same guarded tool. The tool never publishes a course or syncs a Blueprint.
Enabling PowerPoint upload (0.16.0)
Set CANVAS_POWERPOINT_UPLOAD_ROOT to an absolute, current-user-owned folder containing reviewed .pptx files. Restart or reconnect existing MCP clients to discover canvas_upload_powerpoint. The tool uses the enabled local write policy's exact course allowlist and requires APPROVE CANVAS POWERPOINT UPLOAD WRITE course <course_id> after explicit human authorization. Enabling this tool does not authorize uploading unrelated files.
Only standard macro-free .pptx packages up to 25 MiB are accepted. Legacy .ppt, .pptm, arbitrary ZIP files, symlinks, unsafe filenames, encrypted entries, VBA and ActiveX content are rejected. Validation checks the package content type, presentation XML, and root office-document relationship; bounds expanded content and XML metadata; and freezes the validated bytes before any network write. This validates the package format, not the accuracy or safety of every slide's content.
Uploads use the existing authenticated Canvas initialization and token-free HTTPS multipart storage exchange. Completion uses a same-origin authenticated GET as specified in the Canvas file upload documentation. A course-scoped read verifies the file ID, MIME type and size before returning a stable course download URL and the local payload SHA-256. Existing filenames are renamed, never overwritten. If an upload outcome is uncertain, inspect course Files before attempting another upload. No automatic retries or page-publication changes occur.
Enabling image upload
Image upload uses the same local write policy and exact course allowlist, plus CANVAS_IMAGE_UPLOAD_ROOT. The tool refuses relative paths, symlinks, files outside that root, files not owned by the current user, unsupported image types, files whose binary signature disagrees with the extension, and files larger than 10 MiB. The exact confirmation is APPROVE CANVAS IMAGE UPLOAD course <course_id>.
Canvas uses a documented three-step upload exchange: initialize the course file through the authenticated API, send the image without the Canvas access token to Canvas's returned HTTPS storage URL, and authenticate the returned same-origin Canvas completion location. The tool then reads /api/v1/courses/<course_id>/files/<file_id> and checks the MIME type and size before returning a persistent course preview path. It never returns storage signatures or the file-download verifier URL.
Current authoring boundary
This project is the source of truth for the shared local Canvas MCP used by supported chats on this Mac. It can author Pages, Modules and module items, Assignments and assignment Rubrics, Discussions, Announcements, and Classic Quizzes with questions after the normal policy and approval gates. It also provides resource-specific deletion tools for Pages, Assignments, Discussions, Classic Quizzes, Modules, and zero-dependency course-owned Rubrics; deleting a Module removes its item placements but not the underlying course content. Its local-file capabilities are the payload-gated image and PowerPoint uploaders described above. Guarded student deactivation, blank-shell creation, and instructor invitations are supported. Generic file upload, New Quizzes, other enrollment changes, cross-listing, template copying, and broader account/course administration remain unsupported.
See docs/2026-08-06-content-authoring-extension.md for the implementation and end-to-end validation record.
See docs/2026-08-16-image-upload-extension.md for the image-upload threat model and validation record.
See docs/2026-08-24-support-maintenance-extension.md for the first support-maintenance tool contract and validation record.
See docs/2026-09-17-announcement-authoring-extension.md for the scheduled-announcement contract and validation record.
Development
python -m pip install -e .
python -m unittest discover -s tests -v
python -m compileall -q src testsTests mock network and credential boundaries. See AGENTS.md for invariants contributors must preserve and ROADMAP.md for intentionally deferred administrative features.
Usernames, primary email, and SIS IDs (0.15.0)
Use canvas_preview_user_update followed by canvas_update_user_account for explicitly requested changes to one existing user. Policy version 6 adds approved_user_update_targets; each grant binds the exact account/user/login, field, current value, and replacement value. An explicit human request authorizes the temporary target entry without another conversational confirmation. The server still honors local policy and MCP-client approval. Existing policies remain readable; their user-update list defaults to empty.
Install the new source and reconnect the client once for tool discovery. Future requests only need the temporary exact target in the private policy, removed after verification. A username change does not automatically change email or SIS ID. See contract, authorization, recovery, and API references.
Guarded student invitation (0.17.1)
canvas_preview_student_enrollment checks the exact Canvas user, SIS user, SIS course, section, and built-in StudentEnrollment role. After an explicit human request for that student and course, canvas_enroll_student requires the preview confirmation, an enabled local write policy, and MCP-client approval. No per-student policy entry is needed. The request binds course_id, section_id, canvas_user_id, sis_user_id, sis_course_id, and role_id. An identical active or invited enrollment returns a verified no-op. One deleted enrollment with all six target fields matching can be re-enrolled; any other conflicting enrollment blocks the write. A new invitation notifies the student and is read back, allowing Canvas either to revive the deleted record or to retain it alongside one matching live record. An uncertain result must be inspected before any retry. Policy version 8 omits approved_student_enrollment_targets; older policies remain readable, but version 7 entries no longer authorize or restrict student invitations.
Guarded student account creation (0.18.0)
Verify the student's identity, institutional email, and eligibility in the institution's records before requesting account creation. canvas_preview_user_creation takes account_id, name, short_name, sortable_name, email, and a ten-digit sis_user_id string. It checks the active Canvas root account, the operator's login and SIS permissions, and possible duplicate accounts. The same email is used as the login and contact address. Canvas receives a request to send the account notification.
For an explicitly authorized account, add the preview's approval_fingerprint to approved_user_creation_fingerprints in the private version 9 policy, preserving its enabled state and other grants. Earlier policy versions remain readable and grant no account creation. Call canvas_create_user_account with the same fields and the preview's confirmation under MCP-client approval. The write checks for duplicates again and reads back the names, active login, SIS ID, and contact channel. If the write outcome is uncertain, inspect Canvas before any new attempt. Remove the temporary fingerprint after the attempt. Course enrollment is a separate operation.
API-first workflow
Use the connector API for supported reads and authorized changes. Handle exact temporary policy entries and technical confirmation values within the user's existing authorization. If installed tools are missing from the active session, reconnect before using them; do not switch to browser editing solely because of that setup step. Use the in-app browser only for a capability the connector does not expose or for necessary visual inspection, explaining the limitation first. Permission and approval denials apply regardless of the interface and must never be bypassed.
Available Tools
11 toolscanvas_create_assignmentADestructive
Create exactly one Canvas assignment in an approved course. Disabled by default and requires explicit confirmation.
Inputs: name (required), course_id (required), confirmation (required), published, +3 more.
canvas_create_classic_quizADestructive
Create exactly one Canvas Classic Quiz in an approved course. New Quizzes are not supported by this tool. Disabled by default and requires explicit confirmation.
Inputs: title (required), course_id (required), confirmation (required), published, +2 more.
canvas_create_classic_quiz_questionADestructive
Add one question to an existing Canvas Classic Quiz in an approved course. Disabled by default and requires explicit confirmation.
Inputs: quiz_id (required), course_id (required), confirmation (required), question_name (required), +4 more.
canvas_create_discussionADestructive
Create exactly one Canvas discussion in an approved course. Disabled by default and requires explicit confirmation.
Inputs: title (required), message (required), course_id (required), confirmation (required), +2 more.
canvas_create_moduleADestructive
Create exactly one Canvas module in an approved course. Disabled by default and requires explicit confirmation.
Inputs: name (required), course_id (required), confirmation (required), position, +1 more.
canvas_create_module_itemADestructive
Add one existing Canvas content item or external URL to an existing module in an approved course. Disabled by default and requires explicit confirmation.
Inputs: type (required), title (required), course_id (required), module_id (required), +6 more.
canvas_get_current_userARead-only
Read the authenticated Canvas user's profile. Read-only.
Inputs: no named parameters.
canvas_get_write_policyARead-only
Show write-policy state and approved course IDs. Does not expose credentials.
Inputs: no named parameters.
canvas_list_modulesARead-only
List modules and module items for one Canvas course. Read-only.
Inputs: course_id (required).
canvas_read_apiBRead-only
Read one Canvas REST API v1 path with GET. Cannot make changes.
Inputs: path (required), query.
canvas_write_pageADestructive
Create or update exactly one Canvas page. Disabled by default. Requires a local per-course allowlist, exact user confirmation, and approval of this write action in the MCP client. This is not generic Canvas write access.
Inputs: body (required), title (required), course_id (required), confirmation (required), +2 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 11 tools
Each tool targets a distinct Canvas resource and action: user info, module listing, generic API read, write policy, and specific create operations for pages, modules, module items, assignments, discussions, and quizzes. The quiz and quiz-question pair are clearly hierarchical, and the generic read tool is distinct from the specific list tool.
All tools use the 'canvas_' prefix followed by a verb_noun pattern (get_user, list_modules, create_assignment, etc.). Minor deviations include 'read_api' instead of 'get_api' and 'write_page' instead of 'create_page', but the overall pattern is predictable and consistent.
With 11 tools, the set is well-scoped for a Canvas LMS integration focused on reading course structure and creating content items. Each tool has a clear purpose, and the count falls comfortably in the ideal 3-15 range without feeling bloated.
The tool surface covers reading (user, modules, generic API), creation (pages, modules, module items, assignments, discussions, quizzes, quiz questions), and page updates, but lacks explicit update/delete operations for most created entities. The generic canvas_read_api provides a workaround for reads, but delete operations are entirely absent, leaving a noticeable lifecycle gap.
Maintenance
Related MCP Connectors
MCP server for the Inistate platform: module discovery, entry management, and activity submission.
OAuth-protected, read-only-by-default MCP server for provenance-labeled QuillCaddie project memory.
Experimental MCP server for current empirical verification of explicit public HTTPS endpoint claims.
A MCP server built for developers enabling Git based project management with project and personal…
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceRead-only MCP server that provides Claude Desktop with access to converted Canvas course exports, including Markdown files, reports, and search tools for local course content.-
- AlicenseNot gradedqualityCmaintenanceMCP server for Canvas LMS with automatic OAuth authentication. Enables interaction with courses, assignments, grades, modules, discussions, quizzes, files, calendar, messaging, and more without manual API token management.3,092 npm1MIT
- AlicenseAqualityBmaintenanceRead-only MCP server for Canvas LMS that exposes tools to list courses, assignments, grades, submissions, syllabi, announcements, modules, pages, and files, without any write operations.1126 npm1MIT
- AlicenseNot gradedqualityBmaintenanceA local MCP server that exposes Canvas LMS courses, assignments, grading, discussions, modules, pages, files, messaging, and peer reviews to an AI assistant through about 100 Markdown-returning tools, with preview-then-confirm behavior for any write operations.MIT