canvas-bulk-grading
Bulk grading workflows for Canvas LMS assignments using rubrics. Covers single grading, batch grading, and code execution strategies with safety-first dry runs.
Install
npx skills add https://github.com/vishalsachdev/canvas-mcp/tree/main/skills/canvas-bulk-grading
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install vishalsachdev-canvas-mcp@llmmart
git clone https://github.com/vishalsachdev/canvas-mcp.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole vishalsachdev/canvas-mcp collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Canvas Bulk Grading
Grade Canvas LMS assignments efficiently using rubric-based workflows. This skill requires the Canvas MCP server to be running and authenticated with an instructor or TA token.
Prerequisites
- Canvas MCP server running and connected
- Authenticated with an educator (instructor/TA) Canvas API token
- Assignment must exist and have submissions to grade
- Rubric must be created and associated with the assignment with
use_for_grading=true. Usecreate_rubricfor creation andassociate_rubricfor an existing rubric; use the Canvas web UI for editing.
Workflow
Step 1: Gather Assignment and Rubric Information
Before grading, retrieve the assignment details and its rubric criteria.
get_assignment_details(course_identifier, assignment_id)
Then get the rubric. Use get_rubric if the rubric is already linked to the assignment, or list_rubrics to browse all rubrics in the course:
get_rubric(course_identifier, assignment_id=assignment_id)
list_rubrics(course_identifier)
get_rubric(course_identifier, rubric_id=rubric_id)
Record the criterion IDs (often prefixed with underscore, e.g., _8027) and rating IDs from the rubric response. These are required for rubric-based grading.
Step 2: List Submissions
Retrieve all student submissions to determine how many need grading:
list_submissions(course_identifier, assignment_id)
Note the user_id for each submission and the workflow_state (submitted, graded, pending_review). Count the submissions that need grading to determine which strategy to use.
Step 3: Choose a Grading Strategy
Use this decision tree based on the number of submissions to grade:
How many submissions need grading?
|
+-- 1-9 submissions
| Use grade_with_rubric (one call per submission)
|
+-- 10-29 submissions
| Use bulk_grade_submissions (concurrent batch processing)
| Set max_concurrent: 5, rate_limit_delay: 1.0
| ALWAYS run with dry_run: true first
|
+-- 30+ submissions OR custom grading logic needed
Use execute_typescript with bulkGrade function
Grading logic runs locally; only selected output returns to the model
ALWAYS run with dry_run: true first
Strategy A: Single Grading (1-9 submissions)
Call grade_with_rubric once per student:
grade_with_rubric(
course_identifier,
assignment_id,
user_id,
rubric_assessment: {
"criterion_id": {
"points": <number>,
"rating_id": "<string>", // optional
"comments": "<string>" // optional per-criterion feedback
}
},
comment: "Overall feedback" // optional
)
Strategy B: Bulk Grading (10-29 submissions)
Always dry run first. Build the grades dictionary mapping each user ID to their grade data, then validate before submitting:
bulk_grade_submissions(
course_identifier,
assignment_id,
grades: {
"user_id_1": {
"rubric_assessment": {
"criterion_id": {"points": 85, "comments": "Good analysis"}
},
"comment": "Overall feedback"
},
"user_id_2": {
"grade": 92,
"comment": "Excellent work"
}
},
dry_run: true, // VALIDATE FIRST
max_concurrent: 5,
rate_limit_delay: 1.0
)
Review the dry run output. If everything looks correct, re-run with dry_run: false.
Strategy C: Code Execution (30+ submissions)
For large classes or custom grading logic, use execute_typescript to run grading locally. This avoids loading all submission data into the conversation context.
execute_typescript(code: `
import { bulkGrade } from './canvas/grading/bulkGrade.js';
await bulkGrade({
courseIdentifier: "COURSE_ID",
assignmentId: "ASSIGNMENT_ID",
gradingFunction: (submission) => {
// Custom grading logic runs locally -- no token cost
const notebook = submission.attachments?.find(
f => f.filename.endsWith('.ipynb')
);
if (!notebook) return null; // skip ungraded
return {
points: 100,
rubricAssessment: { "_8027": { points: 100 } }
// No `comment` here on purpose -- see Safety Rule 6. Add one only when
// the instructor asked for written feedback, and make it feedback.
};
}
});
`)
Use search_canvas_tools("grading", "signatures") to discover available TypeScript modules and their function signatures before writing code.
Token Efficiency
The three strategies have very different token costs:
| Strategy | When | Token Cost | Why |
|---|---|---|---|
grade_with_rubric |
1-9 submissions | Low | Few round-trips, small payloads |
bulk_grade_submissions |
10-29 submissions | Medium | One call with batch data |
execute_typescript |
30+ submissions | Workload-dependent | Grading logic runs locally; only the code and selected output need to enter model context |
The key insight: as submission count grows, sending grading logic to the server can use less model context than bringing all submission data into the conversation.
Safety Rules
- Always dry run first. For
bulk_grade_submissions, setdry_run: truebefore the real run. Review the output for correctness. - Verify the rubric before grading. Confirm criterion IDs, point ranges, and rating IDs match the assignment rubric. Mismatched IDs cause silent failures or incorrect grades.
- Spot-check before bulk. For Strategy B and C, grade 1-2 submissions manually with
grade_with_rubricfirst. Verify in Canvas that the grade and rubric feedback appear correctly. - Respect rate limits. Use
max_concurrent: 5andrate_limit_delay: 1.0(1 second between batches). Canvas rate limits are approximately 700 requests per 10 minutes. - Do not grade without explicit instructor confirmation. Always present the grading plan (rubric mapping, point values, number of students affected) and wait for approval before submitting grades.
- Never attach a comment the instructor did not ask for. A submission comment is visible to the student in SpeedGrader, it appends on every call rather than replacing, and it cannot be un-sent. "Assign grade 8" means the grade only. Never generate a comment that restates the grade or narrates that grading happened (e.g. "Graded via automated review") — that reads to the student as a bot mark on their work and carries no feedback. Include a comment only when the instructor asked for written feedback, and then make it feedback about the work.
Example Prompts
- "Grade Assignment 5 using the rubric"
- "Show me the rubric for the midterm project and grade all submissions"
- "Bulk grade all ungraded submissions for Assignment 3 -- give full marks on criterion 1 and 80% on criterion 2"
- "How many submissions still need grading for the final paper?"
- "Dry run bulk grading for Assignment 7 so I can review before submitting"
- "Use code execution to grade all 150 homework submissions with custom logic"
Error Recovery
| Error | Cause | Action |
|---|---|---|
| 401 Unauthorized | Token expired or invalid | Regenerate Canvas API token |
| 403 Forbidden | Not an instructor/TA for this course | Verify Canvas role |
| 404 Not Found | Wrong course, assignment, or rubric ID | Re-check IDs with list_assignments or list_rubrics |
| 422 Unprocessable | Invalid rubric assessment format | Verify criterion IDs and point ranges match the rubric |
| Partial failures in bulk | Some grades submitted, others failed | Check each status. Unconfirmed assessments may already be saved: inspect Canvas before retrying to avoid duplicate comments. Retry only confirmed unsaved failures |
Files (canvas-mcp)
-
SKILL.md 7.7 KB
--- name: canvas-bulk-grading description: Bulk grading workflows for Canvas LMS assignments using rubrics. Covers single grading, batch grading, and code execution strategies with safety-first dry runs. --- # Canvas Bulk Grading Grade Canvas LMS assignments efficiently using rubric-based workflows. This skill requires the Canvas MCP server to be running and authenticated with an instructor or TA token. ## Prerequisites - Canvas MCP server running and connected - Authenticated with an **educator** (instructor/TA) Canvas API token - Assignment must exist and have submissions to grade - Rubric must be created and associated with the assignment with `use_for_grading=true`. Use `create_rubric` for creation and `associate_rubric` for an existing rubric; use the Canvas web UI for editing. ## Workflow ### Step 1: Gather Assignment and Rubric Information Before grading, retrieve the assignment details and its rubric criteria. ``` get_assignment_details(course_identifier, assignment_id) ``` Then get the rubric. Use `get_rubric` if the rubric is already linked to the assignment, or `list_rubrics` to browse all rubrics in the course: ``` get_rubric(course_identifier, assignment_id=assignment_id) list_rubrics(course_identifier) get_rubric(course_identifier, rubric_id=rubric_id) ``` Record the **criterion IDs** (often prefixed with underscore, e.g., `_8027`) and **rating IDs** from the rubric response. These are required for rubric-based grading. ### Step 2: List Submissions Retrieve all student submissions to determine how many need grading: ``` list_submissions(course_identifier, assignment_id) ``` Note the `user_id` for each submission and the `workflow_state` (submitted, graded, pending_review). Count the submissions that need grading to determine which strategy to use. ### Step 3: Choose a Grading Strategy Use this decision tree based on the number of submissions to grade: ``` How many submissions need grading? | +-- 1-9 submissions | Use grade_with_rubric (one call per submission) | +-- 10-29 submissions | Use bulk_grade_submissions (concurrent batch processing) | Set max_concurrent: 5, rate_limit_delay: 1.0 | ALWAYS run with dry_run: true first | +-- 30+ submissions OR custom grading logic needed Use execute_typescript with bulkGrade function Grading logic runs locally; only selected output returns to the model ALWAYS run with dry_run: true first ``` ### Strategy A: Single Grading (1-9 submissions) Call `grade_with_rubric` once per student: ``` grade_with_rubric( course_identifier, assignment_id, user_id, rubric_assessment: { "criterion_id": { "points": <number>, "rating_id": "<string>", // optional "comments": "<string>" // optional per-criterion feedback } }, comment: "Overall feedback" // optional ) ``` ### Strategy B: Bulk Grading (10-29 submissions) **Always dry run first.** Build the grades dictionary mapping each user ID to their grade data, then validate before submitting: ``` bulk_grade_submissions( course_identifier, assignment_id, grades: { "user_id_1": { "rubric_assessment": { "criterion_id": {"points": 85, "comments": "Good analysis"} }, "comment": "Overall feedback" }, "user_id_2": { "grade": 92, "comment": "Excellent work" } }, dry_run: true, // VALIDATE FIRST max_concurrent: 5, rate_limit_delay: 1.0 ) ``` Review the dry run output. If everything looks correct, re-run with `dry_run: false`. ### Strategy C: Code Execution (30+ submissions) For large classes or custom grading logic, use `execute_typescript` to run grading locally. This avoids loading all submission data into the conversation context. ``` execute_typescript(code: ` import { bulkGrade } from './canvas/grading/bulkGrade.js'; await bulkGrade({ courseIdentifier: "COURSE_ID", assignmentId: "ASSIGNMENT_ID", gradingFunction: (submission) => { // Custom grading logic runs locally -- no token cost const notebook = submission.attachments?.find( f => f.filename.endsWith('.ipynb') ); if (!notebook) return null; // skip ungraded return { points: 100, rubricAssessment: { "_8027": { points: 100 } } // No `comment` here on purpose -- see Safety Rule 6. Add one only when // the instructor asked for written feedback, and make it feedback. }; } }); `) ``` Use `search_canvas_tools("grading", "signatures")` to discover available TypeScript modules and their function signatures before writing code. ## Token Efficiency The three strategies have very different token costs: | Strategy | When | Token Cost | Why | |----------|------|------------|-----| | `grade_with_rubric` | 1-9 submissions | Low | Few round-trips, small payloads | | `bulk_grade_submissions` | 10-29 submissions | Medium | One call with batch data | | `execute_typescript` | 30+ submissions | Workload-dependent | Grading logic runs locally; only the code and selected output need to enter model context | The key insight: as submission count grows, sending grading logic to the server can use less model context than bringing all submission data into the conversation. ## Safety Rules 1. **Always dry run first.** For `bulk_grade_submissions`, set `dry_run: true` before the real run. Review the output for correctness. 2. **Verify the rubric before grading.** Confirm criterion IDs, point ranges, and rating IDs match the assignment rubric. Mismatched IDs cause silent failures or incorrect grades. 3. **Spot-check before bulk.** For Strategy B and C, grade 1-2 submissions manually with `grade_with_rubric` first. Verify in Canvas that the grade and rubric feedback appear correctly. 4. **Respect rate limits.** Use `max_concurrent: 5` and `rate_limit_delay: 1.0` (1 second between batches). Canvas rate limits are approximately 700 requests per 10 minutes. 5. **Do not grade without explicit instructor confirmation.** Always present the grading plan (rubric mapping, point values, number of students affected) and wait for approval before submitting grades. 6. **Never attach a comment the instructor did not ask for.** A submission comment is visible to the student in SpeedGrader, it *appends* on every call rather than replacing, and it cannot be un-sent. "Assign grade 8" means the grade only. Never generate a comment that restates the grade or narrates that grading happened (e.g. "Graded via automated review") — that reads to the student as a bot mark on their work and carries no feedback. Include a comment only when the instructor asked for written feedback, and then make it feedback about the work. ## Example Prompts - "Grade Assignment 5 using the rubric" - "Show me the rubric for the midterm project and grade all submissions" - "Bulk grade all ungraded submissions for Assignment 3 -- give full marks on criterion 1 and 80% on criterion 2" - "How many submissions still need grading for the final paper?" - "Dry run bulk grading for Assignment 7 so I can review before submitting" - "Use code execution to grade all 150 homework submissions with custom logic" ## Error Recovery | Error | Cause | Action | |-------|-------|--------| | 401 Unauthorized | Token expired or invalid | Regenerate Canvas API token | | 403 Forbidden | Not an instructor/TA for this course | Verify Canvas role | | 404 Not Found | Wrong course, assignment, or rubric ID | Re-check IDs with `list_assignments` or `list_rubrics` | | 422 Unprocessable | Invalid rubric assessment format | Verify criterion IDs and point ranges match the rubric | | Partial failures in bulk | Some grades submitted, others failed | Check each status. Unconfirmed assessments may already be saved: inspect Canvas before retrying to avoid duplicate comments. Retry only confirmed unsaved failures |
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.