Skip to main content
Glama
README.md
# canvas-mcp

A **read-only** [MCP](https://modelcontextprotocol.io) server that gives Claude access to your Canvas LMS courses, so it can help you study from your teachers' actual materials: what's due, what a test covers, explanations of the slides, practice quizzes, and where you're losing points.

It can't submit, post, or change anything in Canvas. Every tool only reads.

## Tools

| Tool | What it's for |
|---|---|
| `list_courses` | Your courses, their ids, and current grades. Claude starts here. |
| `get_upcoming` | Everything due or scheduled across all classes, with submitted/missing status |
| `list_assignments` / `get_assignment` | Due dates, full instructions, rubric, your score, and teacher feedback |
| `get_grades` | Weighted grade breakdown (good for "what do I need on the final?") |
| `get_syllabus` | Syllabus text |
| `list_modules` | Units/weeks and their contents. Usually the best map of a course. |
| `list_pages` / `get_page` | Notes and pages the teacher wrote in Canvas |
| `list_files` / `read_file` | Reads **PDF, DOCX, PPTX (with speaker notes), HTML, text/code**. Long files come back in chunks. |
| `list_announcements` | Recent announcements ("test moved to Tuesday") |
| `get_calendar` | Course calendar for any date range: daily lesson topics, test days, and optionally due dates ("what did we do Tuesday?", "what's been covered since the last test?") |
| `list_discussions` / `get_discussion` | Discussion prompts and replies |
| `list_quizzes` | Quiz/test dates, number of questions, time limits |
| `search_course` | Finds modules, pages, files, and assignments matching a topic |

Prompts (they show up as templates or slash commands in clients that support them): **`study_guide`**, **`quiz_me`**, **`weekly_plan`**.

---

## Step 1: Build it

Needs Node 20+.

```bash
cd canvas-mcp
npm install
npm run build
```

## Step 2: Check your token

Your Canvas URL is the address you log into, for example `https://yourschool.instructure.com`. Leave off any `/api/v1`.

**macOS / Linux**
```bash
CANVAS_BASE_URL=https://yourschool.instructure.com CANVAS_API_TOKEN=xxxx npm run check
```
**Windows (PowerShell)**
```powershell
$env:CANVAS_BASE_URL="https://yourschool.instructure.com"; $env:CANVAS_API_TOKEN="xxxx"; npm run check
```

If the check passes, it prints your Canvas user id and your active courses. A 401 error means the token is wrong or expired. You can make a new one under Canvas → Account → Settings → Approved Integrations → **+ New Access Token**.

## Step 3: Connect it to Claude

Pick whichever option fits how you use Claude.

### Option A: Claude Code (one command)

```bash
claude mcp add canvas --scope user \
  -e CANVAS_BASE_URL=https://yourschool.instructure.com \
  -e CANVAS_API_TOKEN=xxxx \
  -e CANVAS_TZ=America/Indianapolis \
  -- node /ABSOLUTE/PATH/TO/canvas-mcp/dist/index.js
```

### Option B: Claude Desktop

Open the config file from Claude Desktop → Settings → Developer → Edit Config, and add this:

```json
{
  "mcpServers": {
    "canvas": {
      "command": "node",
      "args": ["C:\\Users\\you\\canvas-mcp\\dist\\index.js"],
      "env": {
        "CANVAS_BASE_URL": "https://yourschool.instructure.com",
        "CANVAS_API_TOKEN": "xxxx",
        "CANVAS_TZ": "America/Indianapolis"
      }
    }
  }
}
```
On macOS, use a path like `/Users/you/canvas-mcp/dist/index.js`. Restart Claude Desktop fully after saving.

### Option C: Self-host it and use it from claude.ai (any device, including a Chromebook)

HTTP mode serves MCP at `https://your-host/mcp/<MCP_SECRET>`. claude.ai's servers connect to that URL directly, so it has to be reachable from the public internet.

1. Generate a secret: `openssl rand -hex 24`
2. Deploy the included `Dockerfile` (Coolify: *New Resource → Dockerfile*, or any Docker host). Set these env vars: `CANVAS_BASE_URL`, `CANVAS_API_TOKEN`, `CANVAS_TZ`, `MCP_SECRET`. The container listens on port 7341 and has `/health` for health checks.
3. Expose it on a subdomain, for example `canvas-mcp.yourdomain.com`, through your reverse proxy or Cloudflare Tunnel. **Don't put it behind Cloudflare Access or Basic Auth.** claude.ai can't log in through those, so the secret path is the protection instead.
4. In claude.ai, go to Settings → Connectors, add a custom connector, and paste `https://canvas-mcp.yourdomain.com/mcp/<MCP_SECRET>`.

Without Docker: `node dist/index.js --http` (it reads the same env vars). It binds to 127.0.0.1 by default, so only this machine can reach it; add `--host 0.0.0.0` when a proxy on another machine needs to reach it.

> ⚠️ Anyone who has the full URL can read your Canvas through it. Treat the URL like a password. If it leaks, change `MCP_SECRET`, and also revoke the token in Canvas if you're unsure.

---

## Using it

Example requests:

- "What's due this week across all my classes? Anything missing?"
- "My AP Bio Unit 4 test is Tuesday. Make me a study guide from the actual slides and notes."
- "Quiz me on the APUSH Period 5 material, one question at a time."
- "Explain slide 12 of the cell respiration PowerPoint like I'm confused."
- "What do I need on the final to keep an A in chemistry?"
- "Where did I lose points on my last lab? Read the rubric feedback."

## CLI reference

```
canvas-mcp [--url URL] [--token T] [--tz ZONE] [--max-chars N]   stdio mode (default)
canvas-mcp --http [--port 7341] [--host 127.0.0.1] [--secret S]    HTTP mode
canvas-mcp --check                                                  test credentials
canvas-mcp --help | --version
```

| Env var | Flag | Default |
|---|---|---|
| `CANVAS_BASE_URL` | `--url` | (required) |
| `CANVAS_API_TOKEN` | `--token` | (required; prefer the env var over the flag so the token doesn't land in shell history) |
| `CANVAS_TZ` | `--tz` | system time zone |
| `CANVAS_MAX_CHARS` | `--max-chars` | 20000 characters per response chunk |
| `MCP_SECRET` | `--secret` | (required for `--http`, minimum 24 chars) |
| `CANVAS_MCP_PORT` / `CANVAS_MCP_HOST` | `--port` / `--host` | 7341 / 127.0.0.1 (Docker image uses 0.0.0.0) |

## Limitations

- **Scanned PDFs** (images with no text layer) come back mostly empty, and the tool says so. Download the file and attach it to the chat so Claude can see the pages.
- **Quiz questions** aren't visible to students through the API until results are released. That's Canvas's rule, not something this server can change.
- Some schools hide tabs such as Files or Pages. Those tools return a 403/404, and Claude can work around it with `list_modules`.
- Some schools disable personal access tokens entirely. If you can't make one, this approach won't work at your school.

## Development

```bash
npm run dev     # tsc --watch
npm test        # runs every tool against a mock Canvas API (stdio + HTTP)
```

MIT licensed.