Skip to main content
Glama
README.md
# Canvas Companion

Canvas Companion is a read-only MCP server. It lets your AI assistant (Claude, ChatGPT, Cursor, VS Code and others) read **your own** Canvas account: courses, deadlines, assignments, announcements, files, syllabus, grades and discussions. It works at any school that uses Canvas, with no admin involvement and no institutional developer key. It never asks for or stores your password.

- **Read-only, enforced in code.** Every request it sends to Canvas is a GET. Anything else is refused before it leaves your computer, and tests prove it.
- **Local first.** It runs on your computer, and your sign-in stays in your system keychain. A hosted relay is optional.
- **No AI inside.** The server never calls an AI service and holds no AI keys. It hands Canvas data to the AI app you already use, and that app does the thinking.

```sh
npx canvas-companion panel      # control panel in your browser: connect Canvas, add it to your AI apps
npx canvas-companion setup      # or the same steps in the terminal
```

**Claude Desktop:** download [canvas-companion-mac.mcpb](https://github.com/avaggarwal69-droid/canvas-companion/releases/latest/download/canvas-companion-mac.mcpb) or [canvas-companion-windows.mcpb](https://github.com/avaggarwal69-droid/canvas-companion/releases/latest/download/canvas-companion-windows.mcpb), double-click it and click Install. Then say *"Set up Canvas Companion"* in a chat: Claude asks for your school and opens its normal sign-in window. See [Claude Desktop extension](#claude-desktop-extension).

**Let your AI set it up:** `npx canvas-companion ai-setup` (or the control panel's **Copy prompt** button) gives you a prompt for any AI chat. The assistant walks you through adding Canvas Companion, then finishes with its `setup` tool. You never paste a password or token into the chat.

Then ask your assistant: *"What's due this week?"*, *"Anything new in Chem?"*, *"What do I need on the final?"*, or *"Read the Lecture 12 slides and quiz me."*

---

## Sign in

`setup` asks for your school's name (or your Canvas address), then how to connect:

| Option | What it can read | How long it lasts |
|---|---|---|
| **1. Personal access token** (recommended) | Everything your account can see, via the Canvas API | Until you delete it, or the expiry you chose |
| **2. Browser sign-in** | Same as above | Until your school ends the session (kept alive every 20 min) |
| **3. Calendar feed** | Deadlines and calendar events only | Until you reset the feed in Canvas |

**1. Personal access token.** In Canvas, go to **Account → Settings → Approved Integrations → + New Access Token**. Name it "Canvas Companion" (the expiry is optional), then paste it into setup. Setup opens that page for you. If there is no "New Access Token" button, your school has turned tokens off: use option 2.

**2. Browser sign-in.** Setup opens **your installed Chrome or Edge** on your school's real login page. You sign in as you always do (SSO, MFA), and the window closes by itself.

- **What is kept:** only Canvas's own session cookie. Nothing from your school's sign-in page is read or kept.
- **Keep-alive:** while your AI app is open, a light check every 20 minutes keeps you signed in.
- **When the school ends the session:** you get a notification and a new sign-in window. To sign in again by hand, run `npx canvas-companion login`.

The window uses its own browser profile, so school "remember me" works next time; `login --fresh` uses a throwaway profile instead.

> **Important:** a saved browser session or access token can do anything you can do in Canvas. Canvas Companion only ever reads, and the sign-in is stored in your system keychain, but protect your computer accordingly.

**3. Calendar feed.** In Canvas, open **Calendar** and click **Calendar Feed** (bottom right). Copy the link and paste it into setup. Treat this link like a password.

Check your setup any time with `npx canvas-companion status` (shows the school, your name, sign-in type, time zone and how long the sign-in should last) or `npx canvas-companion doctor`.

## Control panel

```sh
npx canvas-companion panel
```

Opens a page on your own computer (`http://127.0.0.1:<port>`) with everything in one place:

- **Home:** status, the three steps, and **Let your AI set it up** (a prompt to copy into any AI chat).
- **Setup guide:** every step from nothing to *"What's due this week?"*: making a Canvas token, browser sign-in, the calendar feed, then the exact steps for Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Gemini CLI, LM Studio, Codex CLI and the web/phone apps, with a Connect button for each and a troubleshooting list. It shows which steps you've already done.
- **Canvas sign-in:** find your school, then connect with a token, browser sign-in, or calendar feed. Sign out, or remove everything.
- **Connect AI apps:** one click per detected app, the Claude Desktop extension download, and config for anything else.
- **Saved notes:** view and delete.
- **Remote access:** upload to a hosted relay, get sign-in codes, delete from the relay.
- **Settings** and **Privacy**.

How the panel is protected:

- It is reachable only from this computer.
- It opens with a one-time link and refuses other sites' requests.
- It stops by itself after 30 minutes idle, or with the **Stop** button.
- `--no-browser` prints the link instead of opening it.

## Claude Desktop extension

A one-file install for Claude Desktop (macOS and Windows).

1. Get the `.mcpb` file: download it from the control panel's **Connect AI apps** page, or build it with `npm run build:extension` (`-- --target all` builds mac + windows into `dist-extension/`).
2. Double-click it. Claude Desktop shows an install dialog.
3. Every setting is optional. Leave them empty and say *"Set up Canvas Companion"* in a chat: Claude does the rest with the `setup` tool. Or enter your **Canvas address**, plus a **personal access token** if you have one. Claude Desktop keeps the token in your system keychain.
4. With no token, the extension uses the sign-in this computer already has for that school (from the control panel or `setup`). If there is none, the first time you ask about Canvas a sign-in window opens. Sign in as usual and ask again.

The extension runs on Claude Desktop's built-in Node, so you don't need Node installed. Saved notes use the same `~/.canvas-companion` folder as the command-line version. The bundle isn't code-signed yet, so Claude Desktop marks it as unverified.

## Connect your AI apps

```sh
npx canvas-companion connect          # finds your apps and adds Canvas Companion (asks first)
npx canvas-companion connect --print  # config to paste into anything else
```

| App | How |
|---|---|
| Claude Desktop | automatic (`claude_desktop_config.json`) |
| Claude Code | automatic (`claude mcp add --scope user`) |
| Cursor | automatic (`~/.cursor/mcp.json`) |
| VS Code / Insiders | automatic (user `mcp.json`, `servers`) |
| Windsurf | automatic (`~/.codeium/windsurf/mcp_config.json`) |
| Gemini CLI | automatic (`~/.gemini/settings.json`) |
| LM Studio | automatic (`~/.lmstudio/mcp.json`) |
| Codex CLI | automatic (`~/.codex/config.toml`) |
| Zed and other stdio apps | `connect --print`, then paste |
| ChatGPT, claude.ai on the web, phones | need a URL: see [Remote connector](#remote-connector-optional) |

How `connect` handles your config files:

- It only adds or updates its own entry; other servers and settings are kept.
- The original file is backed up once as `<file>.bak-canvas-companion`.
- A file with comments in it is left alone, and the snippet is printed for you to paste.
- **Fully quit and reopen** the app afterwards.
- The config points at the exact Node on your computer, because desktop apps don't see your shell's PATH. Run `connect` again after moving or upgrading Node.
- `connect --remove` takes Canvas Companion out again.

**Local HTTP, for apps on this computer that need a URL:**

```sh
npx canvas-companion serve --http --port 8765 --show-token
# http://127.0.0.1:8765/mcp   header: Authorization: Bearer <token>
```

## What it can do

| Tool | For |
|---|---|
| `check_auth` | Is it signed in, as whom, until when |
| `get_my_courses` | Courses with ids, terms, teachers and scores; warns about lecture and recitation shells; your saved notes |
| `get_deadlines` | Everything due, soonest first, with status and your own override dates. Includes `dueSoon` (with the exact calls that read the instructions and sheet), saved exams, and work mentioned only in announcements |
| `get_assignments` | Instructions, rubric, your submission, comments, linked files. New Quizzes and external tools are flagged, with where the prompt likely is |
| `get_announcements` | Recent announcements with attachments, links and dates found in the text |
| `get_calendar` | Classes, exams, review sessions and due entries; repeated descriptions are printed once |
| `get_course_content` | Modules, text headers, locks and unlock dates, what changed since you last looked |
| `find_content` | Find a file, page or assignment by title across courses |
| `read_content` | Text of PDFs, Word, PowerPoint (with speaker notes), Excel, pages and text files, in budgeted chunks |
| `get_file_page` | Look at pages as images: slides, equations, scans, handwriting |
| `get_syllabus` | The syllabus wherever it lives (tab, module file, front page, Google Doc) |
| `read_link` | A public outside page or doc, safely (https only, no private networks, no sign-in sent) |
| `get_roster` | Instructors and TAs (with email when shown); classmates by name only |
| `get_grades` | Official score, weighted groups, drop rules, per-assignment scores and comments, points left |
| `get_discussions` | Prompts and the newest posts, with older posts on request |
| `get_course_brief` | One read of a course: announcements, instructions, prompts, modules, syllabus, key files, and every date found |
| `get_whats_new` | New announcements, files, pages, grades and due-date changes since you last asked |
| `remember` / `get_memory` | Exams, facts and documents saved for later chats |
| `setup` | Local only. Lets the AI do the setup: finds your school, opens your school's sign-in window or a private page for a token, confirms, and can add Canvas Companion to your other AI apps. It has no field for a password or token. Never on the hosted relay |

Times are always in your time zone, and every result names that zone. Dates found in instructors' prose are marked unverified and kept apart from official due dates. Text written in Canvas is treated as data. Anything that reads like instructions to an AI is flagged so your assistant doesn't follow it.

## Remote connector (optional)

To use Canvas Companion from ChatGPT, claude.ai on the web, or your phone, you need a **hosted relay**. Someone runs it (you, your student group, a service you trust; see [RUNBOOK.md](RUNBOOK.md)), and you opt in:

```sh
npx canvas-companion relay upload --url https://relay.example.edu   # shows what is stored and why; type "yes"
```

1. In your AI app, add a custom connector with the URL `https://relay.example.edu/mcp`.
2. When the app opens the relay's sign-in page, run `npx canvas-companion relay code` and type the code. No password is involved.
3. Approve the app.

On the relay:

- Your sign-in is **encrypted**, your data is kept apart from every other student's, and each app gets its own revocable access.
- `npx canvas-companion relay open` opens your dashboard: connected apps, an activity log (tool and course, never content), **Download my data**, and **Disconnect and delete everything**.
- `relay delete` does the same deletion from the command line.

## Commands

```
panel      control panel in your browser     setup     connect Canvas (terminal)
ai-setup   prompt for your AI chat to walk you through setup
status     what's connected, how long it lasts
login      browser sign-in again             doctor    check this computer's setup
connect    add to your AI apps               serve     run the MCP server (apps do this)
logout     remove sign-in + notes            purge     remove everything stored locally
relay      upload | code | open | status | delete     (optional hosted relay)
```

`logout` can't delete a token inside Canvas, because Canvas Companion never sends anything but reads. It prints the link so you can delete the token yourself.

## What is stored, and where

| What | Local | Hosted relay (only if you opt in) |
|---|---|---|
| Canvas sign-in | System keychain (Linux without a keychain: an encrypted file; `doctor` warns) | Encrypted (AES-256-GCM, with a key held outside the database) |
| School address, name, time zone | `~/.canvas-companion/config.json` (only you can read it) | Your account row |
| Saved notes and "last checked" markers | `~/.canvas-companion/data.db` | Your rows, isolated by row-level security |
| Activity log | none | Tool + course + time, never content |
| Logs | your AI app's MCP log, with no credentials or course text | same rule |

There is no telemetry.

## Safety notes

- [THREAT_MODEL.md](THREAT_MODEL.md): session theft, cross-tenant leakage, prompt injection, malicious files, expiry edge cases.
- [COMPLIANCE.md](COMPLIANCE.md): open legal questions (FERPA, GDPR, minors, school policies). Canvas Companion has **no** compliance certification.
- Canvas Companion is not affiliated with Instructure or your school. Check your school's acceptable-use policy, especially before using a hosted relay.

## Development

```sh
npm install
npm test                               # 276 tests: msw mock of Canvas, PGlite for the relay
npm run build:extension                # Claude Desktop extension (.mcpb) into dist-extension/
npm run test:extension                 # runs the packed .mcpb the way Claude Desktop does (add -- --session for browser sign-in)
npm run smoke                          # builds, then drives the real CLI + MCP over stdio and HTTP
npm run load-test                      # relay under concurrent students (per-school limit check)
node scripts/live-signin-check.mjs     # opens real Chrome against the mock school sign-in
npm run mock                           # mock Canvas on http://127.0.0.1:4010
```

`CANVAS_COMPANION_NOW` pins the clock (testing only). Design decisions are in [DECISIONS.md](DECISIONS.md).