Canvas homework workflow ("Chip")
This is the harness. It connects four things: the canvas MCP server (lists and
reads assignments), the scripts/canvas.mjs helper (downloads files the MCP
can't), the built-in docx and pdf skills / Read tool (read the files), and
the writing-voice skill (draft in Max's voice). Follow the steps in order.
Tools you will use
From the canvas MCP server (this is its complete toolset):
canvas_list_active_courses — fast list of your current courses (dashboard API).
Prefer this for "what am I taking right now."
list_courses — all courses; use flags for completed/all. Use when you need a
course that isn't on the active dashboard.
search_assignments — search assignment titles/descriptions; filter by due-date
range, course, and completion/submission status. Use this to find what's not done.
get_assignment — full details for one assignment; ask for markdown output so
the description and any embedded file links come through cleanly.
The canvas MCP cannot download attachments. That gap is filled by:
node scripts/canvas.mjs assignment-files <courseId> <assignmentId> — start here.
Finds every file linked from the assignment description, its attachments, and any file
sitting in the same module. Dedupes and tells you where each one came from.
node scripts/canvas.mjs files <courseId> [nameFilter] — list downloadable files in a
course, optionally filtered by a substring of the filename. Follows pagination, so it
sees past the first 100 files.
node scripts/canvas.mjs download <fileId|url> [outPath] — download one file into
output/ (resolves Canvas's signed download URL using the API token).
Reading files (all built in — do not install an MCP for these):
.docx → the docx skill (extract full text; never edit the source file).
.pdf → the Read tool (use the pages parameter) or the pdf skill.
Checking a finished draft:
node scripts/lint-draft.mjs <path> — mechanically enforces the writing-voice hard
guardrails (no em dashes, no markdown). Exits non-zero and prints a line:col for every
violation. This is not optional; see Branch B step 4.
Step 1 — List outstanding assignments
- Call
canvas_list_active_courses to get your current courses (and their IDs —
you'll need IDs later). Fall back to list_courses if you need a non-active course.
- Call
search_assignments filtered to not submitted / not completed, ideally
within an upcoming due-date window (e.g. now → +30 days). If the filter options
don't cleanly express "incomplete," pull the candidates and filter by submission
status / due date yourself.
- Show Max a clean list: course → assignment title → due date → points. Sort by
soonest due. If he just asked "what do I have left," stop here and let him
pick one. Don't auto-start drafting everything.
Step 2 — Pull the chosen assignment
Call get_assignment (markdown) for the assignment Max picks. Read the description
in full. Note: the deliverable type (essay, discussion post, worksheet, short
answer), the length/format requirements, the due date, and any rubric text.
Step 3 — Find a downloadable file (usually a DOCX)
Look for an attached file, cheapest path first:
- Ask directly —
node scripts/canvas.mjs assignment-files <courseId> <assignmentId>.
This covers the two cases below in one call, including files that are only reachable
through the assignment's module rather than the course Files tab. Usually this is the
whole step.
- In the description from
get_assignment — scan for links to Canvas files
(URLs containing /files/<id> or instructure_file_link anchors). Grab the file
id or URL.
- In the course files — run
node scripts/canvas.mjs files <courseId> <nameFilter>
and look for a file whose name matches the assignment (e.g. files 1234 essay).
Drop the filter to see everything.
If you find one, download it:
node scripts/canvas.mjs download <fileId|url> output/<sensible-name.ext>
Then decide which branch you're in based on what the file actually is.
Step 4 — Branch on what you found
Branch A — a DOCX exists AND it contains the assignment
This is the "just show it to me" path.
- Read the entire DOCX with the
docx skill (extract text).
- Do NOT edit the file. Leave it exactly as downloaded.
- Print the full assignment text to the console (into the chat), verbatim and
complete — not a summary. If it's long, print all of it anyway; that's the point.
- After printing, offer (one line): "Want me to draft a response in your voice?" —
if yes, continue into Branch B using this DOCX's contents as the prompt.
"Contains the assignment" means the DOCX is the actual prompt/instructions or a
worksheet to read. If the DOCX is empty, or is just a rubric/cover sheet and the
real task lives in the Canvas description, treat it as Branch B instead.
Branch B — no DOCX, the DOCX isn't the assignment, or Max asked for a draft
This is the "do it in my voice" path.
- Figure out the task. From the
get_assignment description, determine exactly
what has to be produced: the deliverable type, the prompt/question, required
length, format, citation style, and any rubric criteria. State it back in one or
two sentences so Max can confirm you understood the assignment.
- Read the context files. If the assignment references a reading or attaches a
PDF (or any other format), download it via
canvas.mjs download and read it
with the Read tool (pages) or pdf skill. Pull out the facts, quotes, and
specifics you'll need so the draft is grounded in the actual source material.
- Draft in Max's voice. Load the
writing-voice skill
(.claude/skills/writing-voice/SKILL.md) and write the response following it:
stance-first, myth-busting, concrete numbers/examples, his punctuation and idiom
habits, register matched to the assignment (academic vs. business/opinion). Hit
the required length and rubric points. Obey the writing-voice hard guardrails: the
draft must contain no em dashes and no markdown formatting (plain prose only). Scan
the finished draft for "—", "*", "_", "#", and backticks before printing.
- Save, lint, then print — in that order:
- Tell Max where it saved, the word count, and which rubric requirements it covers.
Step 5 — Hand-off note
End every Branch B draft with a short, honest reminder (one line, not a lecture):
"This is a first draft in your voice. Read it, make it yours, and check your
course's policy on AI assistance before submitting." This is Max's own writing
style applied to his own assignments; the point is to beat the blank page and study,
not to submit unread.
Guardrails & tips
- Never edit a downloaded assignment file. Read it; don't write back to it.
Drafts go in
output/, originals stay untouched.
- Print, don't just summarize, in Branch A. Max wants the whole thing surfaced.
- If the canvas MCP isn't connected, say so and fall back to
node scripts/canvas.mjs courses / files to confirm credentials work, then point
Max at README.md setup.
- If credentials are missing,
canvas.mjs prints exactly what to set — relay that.
- Course IDs from
list_courses are what canvas.mjs files <courseId> needs; keep
track of them as you go.
1---2name: canvas-homework3description: Check Canvas for outstanding assignments and either surface the assignment, or draft a response in Max's voice. Use whenever Max asks what assignments / homework / coursework he still has to do, asks to pull an assignment's instructions or files from Canvas, or asks to start/draft an assignment. Orchestrates the canvas MCP, the canvas.mjs file-download helper, the built-in docx and pdf readers, and the writing-voice skill.4---56# Canvas homework workflow ("Chip")78This is the harness. It connects four things: the **canvas** MCP server (lists and9reads assignments), the **`scripts/canvas.mjs`** helper (downloads files the MCP10can't), the **built-in `docx` and `pdf` skills / `Read` tool** (read the files), and11the **`writing-voice` skill** (draft in Max's voice). Follow the steps in order.1213## Tools you will use1415From the **canvas** MCP server (this is its complete toolset):16- `canvas_list_active_courses` — fast list of your current courses (dashboard API).17 Prefer this for "what am I taking right now."18- `list_courses` — all courses; use flags for completed/all. Use when you need a19 course that isn't on the active dashboard.20- `search_assignments` — search assignment titles/descriptions; filter by due-date21 range, course, and completion/submission status. Use this to find what's *not done*.22- `get_assignment` — full details for one assignment; ask for **markdown** output so23 the description and any embedded file links come through cleanly.2425The canvas MCP **cannot download attachments.** That gap is filled by:26- `node scripts/canvas.mjs assignment-files <courseId> <assignmentId>` — **start here.**27 Finds every file linked from the assignment description, its attachments, and any file28 sitting in the same module. Dedupes and tells you where each one came from.29- `node scripts/canvas.mjs files <courseId> [nameFilter]` — list downloadable files in a30 course, optionally filtered by a substring of the filename. Follows pagination, so it31 sees past the first 100 files.32- `node scripts/canvas.mjs download <fileId|url> [outPath]` — download one file into33 `output/` (resolves Canvas's signed download URL using the API token).3435Reading files (all built in — **do not** install an MCP for these):36- `.docx` → the **`docx` skill** (extract full text; never edit the source file).37- `.pdf` → the **`Read` tool** (use the `pages` parameter) or the **`pdf` skill**.3839Checking a finished draft:40- `node scripts/lint-draft.mjs <path>` — mechanically enforces the writing-voice hard41 guardrails (no em dashes, no markdown). Exits non-zero and prints a line:col for every42 violation. This is not optional; see Branch B step 4.4344---4546## Step 1 — List outstanding assignments47481. Call `canvas_list_active_courses` to get your current courses (and their IDs —49 you'll need IDs later). Fall back to `list_courses` if you need a non-active course.502. Call `search_assignments` filtered to **not submitted / not completed**, ideally51 within an upcoming due-date window (e.g. now → +30 days). If the filter options52 don't cleanly express "incomplete," pull the candidates and filter by submission53 status / due date yourself.543. Show Max a clean list: **course → assignment title → due date → points**. Sort by55 soonest due. If he just asked "what do I have left," **stop here** and let him56 pick one. Don't auto-start drafting everything.5758## Step 2 — Pull the chosen assignment5960Call `get_assignment` (markdown) for the assignment Max picks. Read the description61in full. Note: the deliverable type (essay, discussion post, worksheet, short62answer), the length/format requirements, the due date, and any rubric text.6364## Step 3 — Find a downloadable file (usually a DOCX)6566Look for an attached file, cheapest path first:671. **Ask directly** — `node scripts/canvas.mjs assignment-files <courseId> <assignmentId>`.68 This covers the two cases below in one call, including files that are only reachable69 through the assignment's module rather than the course Files tab. Usually this is the70 whole step.712. **In the description** from `get_assignment` — scan for links to Canvas files72 (URLs containing `/files/<id>` or `instructure_file_link` anchors). Grab the file73 id or URL.743. **In the course files** — run `node scripts/canvas.mjs files <courseId> <nameFilter>`75 and look for a file whose name matches the assignment (e.g. `files 1234 essay`).76 Drop the filter to see everything.7778If you find one, download it:79```80node scripts/canvas.mjs download <fileId|url> output/<sensible-name.ext>81```82Then decide which branch you're in based on what the file actually is.8384---8586## Step 4 — Branch on what you found8788### Branch A — a DOCX exists AND it contains the assignment8990This is the "just show it to me" path.91921. Read the **entire** DOCX with the `docx` skill (extract text).932. **Do NOT edit the file.** Leave it exactly as downloaded.943. **Print the full assignment text to the console** (into the chat), verbatim and95 complete — not a summary. If it's long, print all of it anyway; that's the point.964. After printing, offer (one line): *"Want me to draft a response in your voice?"* —97 if yes, continue into Branch B using this DOCX's contents as the prompt.9899> "Contains the assignment" means the DOCX is the actual prompt/instructions or a100> worksheet to read. If the DOCX is empty, or is just a rubric/cover sheet and the101> real task lives in the Canvas description, treat it as Branch B instead.102103### Branch B — no DOCX, the DOCX isn't the assignment, or Max asked for a draft104105This is the "do it in my voice" path.1061071. **Figure out the task.** From the `get_assignment` description, determine exactly108 what has to be produced: the deliverable type, the prompt/question, required109 length, format, citation style, and any rubric criteria. State it back in one or110 two sentences so Max can confirm you understood the assignment.1112. **Read the context files.** If the assignment references a reading or attaches a112 **PDF** (or any other format), download it via `canvas.mjs download` and read it113 with the `Read` tool (`pages`) or `pdf` skill. Pull out the facts, quotes, and114 specifics you'll need so the draft is grounded in the actual source material.1153. **Draft in Max's voice.** Load the **`writing-voice`** skill116 (`.claude/skills/writing-voice/SKILL.md`) and write the response following it:117 stance-first, myth-busting, concrete numbers/examples, his punctuation and idiom118 habits, register matched to the assignment (academic vs. business/opinion). Hit119 the required length and rubric points. **Obey the writing-voice hard guardrails: the120 draft must contain no em dashes and no markdown formatting (plain prose only). Scan121 the finished draft for "—", "*", "_", "#", and backticks before printing.**1224. **Save, lint, then print — in that order:**123 - **Save** it to `output/<course>-<assignment-slug>.md`.124 - **Run the guardrail linter** on it:125 ```126 node scripts/lint-draft.mjs output/<course>-<assignment-slug>.md127 ```128 It prints a line:col for every em dash and every markdown character, and exits129 non-zero if it finds any. Fix each one, save, and re-run until it prints `Clean`.130 **Do not print the draft to Max until the linter passes.** Your own scan in step 3131 is the first pass; this is the backstop, and it is the one that actually decides.132 - **Print the full draft to the console** (into the chat).1335. Tell Max where it saved, the word count, and which rubric requirements it covers.134135---136137## Step 5 — Hand-off note138139End every Branch B draft with a short, honest reminder (one line, not a lecture):140*"This is a first draft in your voice. Read it, make it yours, and check your141course's policy on AI assistance before submitting."* This is Max's own writing142style applied to his own assignments; the point is to beat the blank page and study,143not to submit unread.144145---146147## Guardrails & tips148149- **Never edit a downloaded assignment file.** Read it; don't write back to it.150 Drafts go in `output/`, originals stay untouched.151- **Print, don't just summarize, in Branch A.** Max wants the whole thing surfaced.152- If the **canvas** MCP isn't connected, say so and fall back to153 `node scripts/canvas.mjs courses` / `files` to confirm credentials work, then point154 Max at `README.md` setup.155- If credentials are missing, `canvas.mjs` prints exactly what to set — relay that.156- Course IDs from `list_courses` are what `canvas.mjs files <courseId>` needs; keep157 track of them as you go.