Skip to main content
Glama
RyK57

canvas-mcp-server

by RyK57

canvas-mcp-server

MCP-сервер для REST API Canvas LMS. Даёт LLM доступ на чтение к вашим курсам, заданиям, оценкам, сдачам, объявлениям, обсуждениям, модулям, страницам и файлам.

20 инструментов, все только для чтения.

Требования

  • Node.js 18+

  • Учётная запись Canvas в любом учебном заведении

  • Токен доступа из Account → Settings → New Access Token в веб-интерфейсе Canvas

Related MCP server: Canvas LMS MCP Server

Установка

npm install
npm run build

Настройка

У Canvas нет общего API-хоста — каждое учебное заведение запускает свой. Обе переменные ниже обязательны.

{
  "mcpServers": {
    "canvas": {
      "command": "node",
      "args": ["/absolute/path/to/canvas-mcp-server/dist/index.js"],
      "env": {
        "CANVAS_BASE_URL": "https://bcourses.berkeley.edu",
        "CANVAS_ACCESS_TOKEN": "your-token-here"
      }
    }
  }
}

Переменная

Обязательна

По умолчанию

Назначение

CANVAS_BASE_URL

да

—

Хост Canvas вашего учебного заведения, включая схему, без завершающего пути

CANVAS_ACCESS_TOKEN

да

—

Account → Settings → New Access Token

CANVAS_REQUEST_TIMEOUT_MS

нет

30000

Таймаут на запрос

TRANSPORT

нет

stdio

stdio или http

PORT / HOST

нет

3000 / 127.0.0.1

Адрес привязки HTTP-транспорта

MCP_PATH_SECRET

при хостинге

—

Обслуживает конечную точку на /mcp/<secret>. Обязателен, когда HOST не является loopback

ALLOWED_ORIGINS

нет

localhost + claude.ai

Разрешённые источники через запятую

Просмотрите инструменты в интерактивном режиме:

CANVAS_BASE_URL=https://your.canvas CANVAS_ACCESS_TOKEN=your-token npm run inspect

Развёртывание (для мобильных Claude / коннекторов claude.ai)

Claude подключается к пользовательским коннекторам из облака Anthropic, а не с вашего устройства, поэтому мобильным и claude.ai нужен доступ по публичному HTTPS. Claude Code и Claude Desktop — нет; используйте там stdio.

1. Сгенерируйте секрет пути

openssl rand -hex 32

Сервер отказывается запускаться на не-loopback интерфейсе без установленного MCP_PATH_SECRET, потому что публичная конечная точка, содержащая ваш токен Canvas, является открытым прокси к вашей учётной записи. С установленным секретом конечная точка перемещается на /mcp/<secret>, и любой другой путь возвращает 404 — включая неверный секрет, так что сканирование хоста не раскрывает, что там живёт MCP-сервер.

2. Разверните

Включённые Dockerfile и railway.json работают как есть на Railway, Render или Fly. Образ устанавливает TRANSPORT=http и HOST=0.0.0.0 и запускается от непривилегированного пользователя. Установите три переменные в панели управления платформы:

Переменная

Значение

CANVAS_BASE_URL

хост Canvas вашего учебного заведения

CANVAS_ACCESS_TOKEN

ваш токен

MCP_PATH_SECRET

значение из шага 1

PORT внедряется платформой. /healthz — неаутентифицированный зонд живости.

3. Проверьте

curl -s https://your-app.up.railway.app/healthz

4. Добавьте коннектор

На claude.ai в браузере — коннекторы нельзя добавить из мобильного приложения:

  1. Customize → Connectors → Add custom connector

  2. URL: https://your-app.up.railway.app/mcp/<secret>

  3. На телефоне откройте чат и включите его в разделе + → Connectors

Относитесь к этому URL как к паролю. Если он утёк, смените MCP_PATH_SECRET и добавьте коннектор заново.

Инструменты

Курсы — canvas_list_courses, canvas_get_course, canvas_get_grades, canvas_list_enrollments, canvas_get_profile

Задания — canvas_list_assignments, canvas_get_assignment, canvas_get_submission, canvas_list_quizzes

Планировщик — canvas_list_planner_items, canvas_list_upcoming, canvas_list_calendar_events

Объявления и обсуждения — canvas_list_announcements, canvas_list_discussions, canvas_get_discussion

Содержимое курса — canvas_list_modules, canvas_list_module_items, canvas_list_pages, canvas_get_page, canvas_list_files

Каждый инструмент чтения принимает response_format: "markdown" | "json". Markdown используется по умолчанию и оптимизирован для чтения LLM; JSON — это полная структурированная полезная нагрузка. structuredContent всегда заполняется независимо от формата.

Примеры

«Что нужно сдать на этой неделе?» → canvas_list_planner_items с end_date на неделю вперёд. Охватывает все курсы одним вызовом и сообщает состояние сдачи. По умолчанию он начинается с сегодняшнего дня, поэтому для «что у меня просрочено» передайте явный более ранний start_date.

«Какие у меня оценки?» → canvas_get_grades. Один вызов, все активные курсы, текущий балл и буквенная оценка.

«Что объявили мои преподаватели на этой неделе?» → canvas_list_courses для идентификаторов, затем canvas_list_announcements со всеми ними сразу.

«Что мне на самом деле нужно сделать для проекта 2?» → canvas_list_assignments с search_term="project 2" для получения идентификатора, затем canvas_get_assignment для полных инструкций.

Замечания по дизайну

Только для чтения по построению. Каждый инструмент несёт readOnlyHint: true и destructiveHint: false, и у клиента нет открытого пути записи. Токены Canvas несут полные полномочия вашей учётной записи — они могут отправлять задания, публиковать сообщения в обсуждениях и изменять настройки профиля — поэтому сервер намеренно отказывается раскрывать что-либо из этого. Тест проверяет это: если когда-либо будет добавлен инструмент записи, набор тестов завершится ошибкой.

Базовый URL обязателен, а не задан по умолчанию. В отличие от однопользовательских API, Canvas запускает один экземпляр на учебное заведение. Разумного значения по умолчанию нет, и токен, выданный Canvas одной школы, бессмысленен в другой, поэтому сервер завершается с ошибкой при запуске, а не вводит вас в заблуждение ошибками 401 позже.

Пагинация живёт в заголовке. Canvas сообщает «есть ли следующая страница» в заголовке Link RFC 5988 и никогда не возвращает общее количество. Эти URL документированы как непрозрачные, поэтому has_more читается из заголовка, а page/per_page остаются элементами управления для вызывающей стороны — агент получает простой next_page для перехода вместо курсора для навигации.

Идентификаторы запрашиваются как строки. Идентификаторы Canvas — 64-битные целые числа, которые JavaScript не может представить точно. Клиент отправляет Accept: application/json+canvas-string-ids, и Canvas выполняет это, возвращая каждый идентификатор строкой, поэтому идентификаторы переживают JSON-кругосветку без изменений.

HTML уплощается до того, как достигает модели. Описания заданий, объявления, сообщения обсуждений и страницы хранятся в HTML. Передача этого дословно сжигает огромный контекст на разметке, поэтому теги превращаются в разрывы строк, сущности декодируются, а длинные тела сокращаются с сохранением html_url для полной версии.

include[] не раскрывается. У Canvas два десятка опций include, они различаются между конечными точками списка и отдельного курса, и большинство управляют полями, которые агенту не нужны. Каждый инструмент запрашивает то, что ему нужно, и показывает только переключатели, меняющие то, что увидит пользователь — include_syllabus, include_grades, include_submission.

Идентификаторы курсов нормализуются в коды контекста. Некоторые конечные точки Canvas обращаются к курсам как course_1234, а не 1234. Обе формы принимаются везде и преобразуются, поэтому агенту не нужно запоминать, какая конечная точка какую хочет.

Ошибки приводят к следующим действиям. Ошибка 404 называет инструмент, который выдаёт допустимые идентификаторы для этого ресурса. Ошибка 403 отличает проблему с правами доступа от исчерпанного лимита запросов, который Canvas сбивающе возвращает под тем же статусом. Ошибка 401 указывает, что токен из Canvas одной школы не будет работать в другой.

Две особенности Canvas обрабатываются, а не передаются дальше. Оценка, которую курс сообщает в enrollments[].computed_current_score, — это то же число, которое API Enrollments называет grades.current_score; читаются оба. А поле submissions элемента планировщика — это логическое false — не объект — когда нечего сдавать, что проверяется перед чтением.

Предостережения

  • Объявления нельзя получить глобально: Canvas требует хотя бы один идентификатор курса, поэтому сначала должен выполняться canvas_list_courses.

  • canvas_list_discussions применяет свой фильтр scope после пагинации, поэтому отфильтрованная страница может вернуться короче, чем per_page, не будучи концом результатов.

  • Canvas опускает элементы модулей из ответа списка для модулей, которые считает большими; canvas_list_module_items получает их.

  • Страницы адресуются по url-слагу (week-1-reading), а не по заголовку. canvas_list_pages возвращает слаг в своём поле url.

  • Конечная точка календаря принимает не более 10 курсов и молча игнорирует остальные; canvas_list_calendar_events сообщает, когда усекает.

  • Оценки отражают только то, что опубликовал преподаватель, и полностью опускаются для курсов, настроенных скрывать итоговые оценки.

Структура проекта

src/
├── index.ts               # entry point, transport selection
├── constants.ts           # enum values, limits, character limit
├── types.ts               # interfaces for every Canvas entity
├── services/
│   └── canvas-client.ts   # fetch wrapper, auth, Link pagination, error → guidance mapping
├── schemas/
│   ├── inputs.ts          # Zod input schemas
│   └── outputs.ts         # structuredContent schemas
├── formatters/
│   ├── response.ts        # pagination, truncation, HTML flattening, format dispatch
│   └── entities.ts        # per-entity markdown rendering
└── tools/
    ├── courses.ts
    ├── assignments.ts
    ├── planner.ts
    ├── announcements.ts
    └── content.ts

Тесты

npm run build
npm test            # 43 checks: MCP handshake, tools, pagination, formatting, errors (mocked API)
npm run test:http   # 19 checks: config validation, path-secret gating, method handling, origins

Оба набора тестов запускаются против локального макета, поэтому не нужны ни токен, ни доступ к сети.

Related MCP Connectors

Related MCP Servers