{"id":"fe4f94fa-50e1-40a4-ad1b-e3b1e1d27261","shortId":"ZbZLQU","kind":"skill","title":"canvas-bulk-grading","tagline":"Bulk grading workflows for Canvas LMS assignments using rubrics. Covers single grading, batch grading, and code execution strategies with safety-first dry runs.","description":"# Canvas Bulk Grading\n\nGrade 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.\n\n## Prerequisites\n\n- Canvas MCP server running and connected\n- Authenticated with an **educator** (instructor/TA) Canvas API token\n- Assignment must exist and have submissions to grade\n- Rubric must already be created in Canvas and associated with the assignment (Canvas API cannot reliably create rubrics -- use the Canvas web UI for that)\n\n## Workflow\n\n### Step 1: Gather Assignment and Rubric Information\n\nBefore grading, retrieve the assignment details and its rubric criteria.\n\n```\nget_assignment_details(course_identifier, assignment_id)\n```\n\nThen get the rubric. Use `get_assignment_rubric_details` if the rubric is already linked to the assignment, or `list_all_rubrics` to browse all rubrics in the course:\n\n```\nget_assignment_rubric_details(course_identifier, assignment_id)\nlist_all_rubrics(course_identifier)\nget_rubric_details(course_identifier, rubric_id)\n```\n\nRecord the **criterion IDs** (often prefixed with underscore, e.g., `_8027`) and **rating IDs** from the rubric response. These are required for rubric-based grading.\n\n### Step 2: List Submissions\n\nRetrieve all student submissions to determine how many need grading:\n\n```\nlist_submissions(course_identifier, assignment_id)\n```\n\nNote 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.\n\n### Step 3: Choose a Grading Strategy\n\nUse this decision tree based on the number of submissions to grade:\n\n```\nHow many submissions need grading?\n|\n+-- 1-9 submissions\n|   Use grade_with_rubric (one call per submission)\n|\n+-- 10-29 submissions\n|   Use bulk_grade_submissions (concurrent batch processing)\n|   Set max_concurrent: 5, rate_limit_delay: 1.0\n|   ALWAYS run with dry_run: true first\n|\n+-- 30+ submissions OR custom grading logic needed\n    Use execute_typescript with bulkGrade function\n    99.7% token savings -- grading logic runs locally\n    ALWAYS run with dry_run: true first\n```\n\n### Strategy A: Single Grading (1-9 submissions)\n\nCall `grade_with_rubric` once per student:\n\n```\ngrade_with_rubric(\n  course_identifier,\n  assignment_id,\n  user_id,\n  rubric_assessment: {\n    \"criterion_id\": {\n      \"points\": <number>,\n      \"rating_id\": \"<string>\",    // optional\n      \"comments\": \"<string>\"      // optional per-criterion feedback\n    }\n  },\n  comment: \"Overall feedback\"     // optional\n)\n```\n\n### Strategy B: Bulk Grading (10-29 submissions)\n\n**Always dry run first.** Build the grades dictionary mapping each user ID to their grade data, then validate before submitting:\n\n```\nbulk_grade_submissions(\n  course_identifier,\n  assignment_id,\n  grades: {\n    \"user_id_1\": {\n      \"rubric_assessment\": {\n        \"criterion_id\": {\"points\": 85, \"comments\": \"Good analysis\"}\n      },\n      \"comment\": \"Overall feedback\"\n    },\n    \"user_id_2\": {\n      \"grade\": 92,\n      \"comment\": \"Excellent work\"\n    }\n  },\n  dry_run: true,          // VALIDATE FIRST\n  max_concurrent: 5,\n  rate_limit_delay: 1.0\n)\n```\n\nReview the dry run output. If everything looks correct, re-run with `dry_run: false`.\n\n### Strategy C: Code Execution (30+ submissions)\n\nFor large classes or custom grading logic, use `execute_typescript` to run grading locally. This avoids loading all submission data into the conversation context.\n\n```\nexecute_typescript(code: `\n  import { bulkGrade } from './canvas/grading/bulkGrade.js';\n\n  await bulkGrade({\n    courseIdentifier: \"COURSE_ID\",\n    assignmentId: \"ASSIGNMENT_ID\",\n    gradingFunction: (submission) => {\n      // Custom grading logic runs locally -- no token cost\n      const notebook = submission.attachments?.find(\n        f => f.filename.endsWith('.ipynb')\n      );\n\n      if (!notebook) return null; // skip ungraded\n\n      return {\n        points: 100,\n        rubricAssessment: { \"_8027\": { points: 100 } },\n        comment: \"Graded via automated review\"\n      };\n    }\n  });\n`)\n```\n\nUse `search_canvas_tools(\"grading\", \"signatures\")` to discover available TypeScript modules and their function signatures before writing code.\n\n## Token Efficiency\n\nThe three strategies have very different token costs:\n\n| Strategy | When | Token Cost | Why |\n|----------|------|------------|-----|\n| `grade_with_rubric` | 1-9 submissions | Low | Few round-trips, small payloads |\n| `bulk_grade_submissions` | 10-29 submissions | Medium | One call with batch data |\n| `execute_typescript` | 30+ submissions | Minimal | Grading logic runs locally; only the code string is sent. **99.7% savings** vs loading all submissions into context |\n\nThe key insight: as submission count grows, sending grading logic to the server (code execution) is far cheaper than bringing all submission data into the conversation.\n\n## Safety Rules\n\n1. **Always dry run first.** For `bulk_grade_submissions`, set `dry_run: true` before the real run. Review the output for correctness.\n2. **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.\n3. **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.\n4. **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.\n5. **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.\n\n## Example Prompts\n\n- \"Grade Assignment 5 using the rubric\"\n- \"Show me the rubric for the midterm project and grade all submissions\"\n- \"Bulk grade all ungraded submissions for Assignment 3 -- give full marks on criterion 1 and 80% on criterion 2\"\n- \"How many submissions still need grading for the final paper?\"\n- \"Dry run bulk grading for Assignment 7 so I can review before submitting\"\n- \"Use code execution to grade all 150 homework submissions with custom logic\"\n\n## Error Recovery\n\n| Error | Cause | Action |\n|-------|-------|--------|\n| 401 Unauthorized | Token expired or invalid | Regenerate Canvas API token |\n| 403 Forbidden | Not an instructor/TA for this course | Verify Canvas role |\n| 404 Not Found | Wrong course, assignment, or rubric ID | Re-check IDs with `list_assignments` or `list_all_rubrics` |\n| 422 Unprocessable | Invalid rubric assessment format | Verify criterion IDs and point ranges match the rubric |\n| Partial failures in bulk | Some grades submitted, others failed | Check the response for per-student status; retry only failed ones |","tags":["canvas","bulk","grading","mcp","vishalsachdev","agent-skills","skills-sh"],"capabilities":["skill","source-vishalsachdev","skill-canvas-bulk-grading","topic-agent-skills","topic-skills-sh"],"categories":["canvas-mcp"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/vishalsachdev/canvas-mcp/canvas-bulk-grading","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add vishalsachdev/canvas-mcp","source_repo":"https://github.com/vishalsachdev/canvas-mcp","install_from":"skills.sh"}},"qualityScore":"0.509","qualityRationale":"deterministic score 0.51 from registry signals: · indexed on github topic:agent-skills · 118 github stars · SKILL.md body (6,877 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-02T12:55:01.169Z","embedding":null,"createdAt":"2026-04-18T22:12:02.048Z","updatedAt":"2026-05-02T12:55:01.169Z","lastSeenAt":"2026-05-02T12:55:01.169Z","tsv":"'-2':717 '-29':289,386,597 '-9':278,345,584 '/canvas/grading/bulkgrade.js':503 '1':110,277,344,418,583,656,716,749,824 '1.0':305,450,748 '10':288,385,596,761 '100':537,541 '150':859 '2':208,433,678,829 '3':255,704,818 '30':313,471,607 '4':736 '401':870 '403':880 '404':891 '422':911 '5':301,446,743,763,795 '7':846 '700':758 '80':826 '8027':191,539 '85':424 '92':435 '99.7':326,620 'action':869 'affect':783 'alreadi':85,146 'alway':306,333,388,657,771 'analysi':427 'api':73,96,878 'appear':734 'approv':787 'approxim':757 'assess':364,420,915 'assign':11,35,75,94,112,120,127,131,139,150,163,168,225,359,413,510,694,794,817,845,896,906 'assignmentid':509 'associ':91 'authent':53,67 'autom':545 'avail':555 'avoid':488 'await':504 'b':382,712 'base':40,205,264 'batch':17,296,603,752 'bring':647 'brows':156 'build':392 'bulk':3,5,30,292,383,408,593,662,709,811,842,929 'bulkgrad':324,501,505 'c':468,714 'call':285,347,601 'cannot':97 'canva':2,9,29,33,46,61,72,89,95,103,549,727,753,877,889 'canvas-bulk-grad':1 'caus':698,868 'cheaper':645 'check':707,902,935 'choos':256 'class':475 'code':20,469,499,564,616,641,854 'comment':371,377,425,428,436,542 'concurr':295,300,445,742 'confirm':684,770 'connect':66 'const':522 'context':496,627 'convers':495,653 'correct':459,677,735 'cost':521,574,578 'count':242,633 'cours':129,161,166,173,178,223,357,411,507,887,895 'courseidentifi':506 'cover':14 'creat':87,99 'criteria':125 'criterion':184,365,375,421,685,823,828,918 'custom':316,477,514,863 'data':403,492,604,650 'decis':262 'delay':304,449,747 'detail':121,128,141,165,177 'determin':216,249 'dictionari':395 'differ':572 'discov':554 'dri':27,309,336,389,439,453,464,658,666,840 'e.g':190 'educ':70 'effici':36,566 'error':865,867 'everyth':457 'exampl':791 'excel':437 'execut':21,321,470,481,497,605,642,855 'exist':77 'expir':873 'explicit':768 'f':526 'f.filename.endswith':527 'fail':934,945 'failur':700,927 'fals':466 'far':644 'feedback':376,379,430,733 'final':838 'find':525 'first':26,312,339,391,443,660,724 'forbidden':881 'format':916 'found':893 'full':820 'function':325,560 'gather':111 'get':126,134,138,162,175 'give':819 'good':426 'grade':4,6,16,18,31,32,82,117,206,220,239,247,258,271,276,281,293,317,329,343,348,354,384,394,402,409,415,434,478,485,515,543,551,580,594,610,636,663,683,703,715,721,730,766,774,790,793,808,812,835,843,857,931 'gradingfunct':512 'grow':634 'homework':860 'id':132,169,181,185,194,226,230,360,362,366,369,399,414,417,422,432,508,511,686,691,697,899,903,919 'identifi':130,167,174,179,224,358,412 'import':500 'incorrect':702 'inform':115 'insight':630 'instructor':56,769 'instructor/ta':71,884 'invalid':875,913 'ipynb':528 'key':629 'larg':474 'limit':303,448,739,746,755 'link':147 'list':152,170,209,221,905,908 'lms':10,34 'load':489,623 'local':332,486,518,613 'logic':318,330,479,516,611,637,864 'look':458 'low':586 'mani':218,273,831 'manual':719 'map':396,777 'mark':821 'match':692,923 'max':299,444,741 'mcp':47,62 'medium':599 'midterm':805 'minim':609 'minut':762 'mismatch':696 'modul':557 'must':76,84 'need':219,246,275,319,834 'note':227 'notebook':523,530 'null':532 'number':267,780 'often':186 'one':284,600,946 'option':370,372,380 'other':933 'output':455,675 'overal':378,429 'paper':839 'partial':926 'payload':592 'pend':240 'per':286,352,374,760,940 'per-criterion':373 'per-stud':939 'plan':775 'point':367,423,536,540,687,778,921 'prefix':187 'prerequisit':60 'present':772 'process':297 'project':806 'prompt':792 'rang':688,922 'rate':193,302,368,447,690,738,745,754 're':461,901 're-check':900 're-run':460 'real':671 'record':182 'recoveri':866 'regener':876 'reliabl':98 'request':759 'requir':44,201 'respect':737 'respons':198,937 'retri':943 'retriev':118,211 'return':531,535 'review':241,451,546,673,850 'role':890 'round':589 'round-trip':588 'rubric':13,39,83,100,114,124,136,140,144,154,158,164,172,176,180,197,204,283,350,356,363,419,582,681,695,723,732,776,798,802,898,910,914,925 'rubric-bas':38,203 'rubricassess':538 'rule':655 'run':28,51,64,307,310,331,334,337,390,440,454,462,465,484,517,612,659,667,672,841 'safeti':25,654 'safety-first':24 'save':328,621 'search':548 'second':750 'send':635 'sent':619 'server':48,63,640 'set':298,665 'show':799 'signatur':552,561 'silent':699 'singl':15,342 'skill':43 'skill-canvas-bulk-grading' 'skip':533 'small':591 'source-vishalsachdev' 'spot':706 'spot-check':705 'state':237 'status':942 'step':109,207,254 'still':833 'strategi':22,251,259,340,381,467,569,575,711 'string':617 'student':213,353,782,941 'submiss':80,210,214,222,233,244,269,274,279,287,290,294,314,346,387,410,472,491,513,585,595,598,608,625,632,649,664,718,810,815,832,861 'submission.attachments':524 'submit':238,407,789,852,932 'ta':58 'three':568 'token':59,74,327,520,565,573,577,872,879 'tool':550 'topic-agent-skills' 'topic-skills-sh' 'tree':263 'trip':590 'true':311,338,441,668 'typescript':322,482,498,556,606 'ui':105 'unauthor':871 'underscor':189 'ungrad':534,814 'unprocess':912 'use':12,37,101,137,253,260,280,291,320,480,547,740,796,853 'user':229,361,398,416,431 'valid':405,442 'valu':779 'verifi':679,725,888,917 'via':544 'vs':622 'wait':785 'web':104 'without':767 'work':438 'workflow':7,41,108,236 'write':563 'wrong':894","prices":[{"id":"8c817504-e218-420d-921d-fcf55856f5c9","listingId":"fe4f94fa-50e1-40a4-ad1b-e3b1e1d27261","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"vishalsachdev","category":"canvas-mcp","install_from":"skills.sh"},"createdAt":"2026-04-18T22:12:02.048Z"}],"sources":[{"listingId":"fe4f94fa-50e1-40a4-ad1b-e3b1e1d27261","source":"github","sourceId":"vishalsachdev/canvas-mcp/canvas-bulk-grading","sourceUrl":"https://github.com/vishalsachdev/canvas-mcp/tree/main/skills/canvas-bulk-grading","isPrimary":false,"firstSeenAt":"2026-04-18T22:12:02.048Z","lastSeenAt":"2026-05-02T12:55:01.169Z"}],"details":{"listingId":"fe4f94fa-50e1-40a4-ad1b-e3b1e1d27261","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"vishalsachdev","slug":"canvas-bulk-grading","github":{"repo":"vishalsachdev/canvas-mcp","stars":118,"topics":["agent-skills","skills-sh"],"license":"mit","html_url":"https://github.com/vishalsachdev/canvas-mcp","pushed_at":"2026-04-27T16:16:22Z","description":"Canvas LMS MCP server — 80+ tools and 5 agent skills for students & educators. Works with Claude, Cursor, Codex, and 40+ agents. v1.1.0","skill_md_sha":"34e3291f45161c2c8e5fd4d33d2603dff451dedf","skill_md_path":"skills/canvas-bulk-grading/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/vishalsachdev/canvas-mcp/tree/main/skills/canvas-bulk-grading"},"layout":"multi","source":"github","category":"canvas-mcp","frontmatter":{"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."},"skills_sh_url":"https://skills.sh/vishalsachdev/canvas-mcp/canvas-bulk-grading"},"updatedAt":"2026-05-02T12:55:01.169Z"}}