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

Локальный MCP-сервер (stdio) для инфраструктуры НИУ ВШЭ: SmartLMS (edu.hse.ru) и Единый личный
кабинет (lk.hse.ru). Расписание RUZ (`ruz.hse.ru`) вынесено в отдельный сервер
[hse-ruz-mcp](https://github.com/ihavealotofguap/hse-ruz-mcp) (`claude mcp add hse-ruz-mcp -- npx -y hse-ruz-mcp`).

## Дисклеймер

- Неофициальный проект: не связан с НИУ ВШЭ и не одобрен ею.
- Правила ВШЭ об автоматизированном доступе к SmartLMS и ЕЛК не проверялись; за их соблюдение отвечает
  пользователь.
- Использование на свой риск, без гарантий (см. лицензию).
- Тулы записи отправляют данные от вашего имени и необратимы: сдача может уйти на оценку сразу, заказ
  справки отменить нельзя. По умолчанию они выключены (`HSE_ENABLE_WRITES`).
- Авторы не хранят и не получают ничьих учётных данных: пароль, сессия и журнал остаются на вашей машине
  (подробности ниже и в `SECURITY.md`).

## Установка и запуск

Нужен аккаунт ВШЭ. Каждый пользователь ставит сервер у себя со своими данными.

### Claude Desktop — без терминала

1. Скачайте `hse-mcp-<версия>.mcpb` из последнего [релиза](https://github.com/ihavealotofguap/hse-mcp/releases/latest).
2. Откройте файл двойным щелчком (или перетащите в окно Claude Desktop, или **Настройки → Расширения →
   Дополнительно → Установить расширение…**).
3. Введите email и пароль ВШЭ и нажмите «Установить». Пароль Claude Desktop хранит в системной связке
   ключей и передаёт серверу при запуске.

Node.js ставить не нужно: он входит в Claude Desktop для Windows и macOS. Для входа нужен браузер:
сервер берёт Microsoft Edge (есть на любой Windows) или Google Chrome, а если их нет, при первом запросе
сам скачивает Chromium (около 100 МБ, один раз) — тогда первый запрос попросит повторить его через
пару минут. Галочка «Разрешить отправку данных» включает тулы записи (см. «Тулы записи»); по
умолчанию она выключена. Файлы для сдачи заданий кладутся в папку из настроек расширения (по умолчанию
`Документы/hse-mcp-outbox`, создаётся сама).

### Через npx (нужен Node.js 22+)

```bash
npx -y hse-mcp credentials set   # email и пароль ВШЭ → системное хранилище
claude mcp add hse-mcp -e HSE_EMAIL=login@edu.hse.ru -- npx -y hse-mcp
```

Для Claude Desktop, Cursor и других клиентов — в их конфигурацию MCP:

```json
{
  "mcpServers": {
    "hse-mcp": {
      "command": "npx",
      "args": ["-y", "hse-mcp"],
      "env": { "HSE_EMAIL": "login@edu.hse.ru" }
    }
  }
}
```

Пароль в этот файл не пишите: он хранится открытым текстом. `npx -y hse-mcp install-browser` скачивает
Chromium заранее (иначе — как выше: Edge, Chrome или скачивание при первом запросе).

### Из исходников (для разработки)

Нужен Node.js 22 или новее.

```bash
npm ci
npx playwright install chromium
cp .env.example .env      # заполнить HSE_EMAIL
npm run credentials:set   # пароль в системное хранилище (рекомендуется)
npm run build
claude mcp add hse-mcp -- node <путь-к-проекту>/dist/index.js
```

`HSE_PASSWORD` в `.env` необязателен: это запасной путь, если системное хранилище недоступно (см.
«Учётные данные»). `.env` и `storage/` в `.gitignore` — не коммитятся, локальны для установки и никуда
не отправляются, кроме как в сами сервисы ВШЭ при логине.

### Каталог данных

Сессия браузера, ключ и журнал записей, снимки `hse_changes` и папка «исходящих» по умолчанию лежат в
каталоге данных:

- в клоне репозитория (есть `.git`) или если `storage/` уже есть — `storage/` в корне проекта;
- иначе (`.mcpb`, npx) — `%LOCALAPPDATA%\hse-mcp` на Windows, `~/Library/Application Support/hse-mcp` на
  macOS, `~/.local/share/hse-mcp` на Linux: папку установки хост может пересоздать при обновлении;
- `HSE_DATA_DIR` задаёт каталог явно (абсолютный путь или относительно корня проекта).

Необязательные переменные (`.env` или окружение): `HSE_ENABLE_WRITES=true` включает тулы записи (по
умолчанию выключены; см. «Тулы записи»), `HSE_OUTBOX_DIR` задаёт папку «исходящих»,
`HSE_BROWSER_CHANNEL` — браузер: `auto` (по умолчанию: скачанный Chromium, затем Edge и Chrome, затем
скачивание), `chromium`, `chrome` или `msedge`. Проверка, что защита записи цела: `npm test` (ожидается
`fail 0`; тесты не ходят в сеть и не читают учётные данные).

## Учётные данные

Пароль ищется по порядку, берётся первый найденный:

1. переменная окружения процесса `HSE_PASSWORD` (в том числе заданная клиентом MCP);
2. системное хранилище: Windows Credential Manager, macOS Keychain, Linux Secret Service (служба
   `hse-mcp`, учётная запись — значение `HSE_EMAIL`), через необязательную зависимость `@napi-rs/keyring`;
3. `HSE_PASSWORD` в `.env` — запасной путь.

`HSE_EMAIL` не секрет и берётся из окружения или `.env`.

Команды:

- `npm run credentials:set` (или `npx hse-mcp credentials set`) — спрашивает пароль без вывода на экран и
  пишет в системное хранилище;
- `npm run credentials:status` (`npx hse-mcp credentials status`) — печатает только источник пароля
  (`env`, `keychain`, `dotenv`, `none`), без значений и без email;
- `npm run credentials:clear` (`npx hse-mcp credentials clear`) — удаляет запись из хранилища.

В пакете `.mcpb` пароль приходит из настроек расширения через окружение (`HSE_PASSWORD`), системное
хранилище hse-mcp там не используется.

Если пароль взят из `.env`, сервер один раз пишет в stderr предупреждение (без значения); на Linux и macOS
он же предупреждает, если права `.env` допускают чтение группой или другими (исправление: `chmod 600 .env`).
На Windows права `.env` сервер не проверяет; ограничьте их вручную, подставив своё имя пользователя:

```powershell
icacls .env /inheritance:r /grant:r "$($env:USERNAME):(F)"
```

Каталог данных (`storage/` в клоне) и его `writes/` сервер ограничивает владельцем (`0o700` на POSIX, `icacls` с
наследованием на Windows, чтобы новые файлы сразу создавались с ограниченными правами), а файлы
`hse-session.json` и `writes/key` — `0o600` / `icacls` (ошибки при этом не выводятся).

Системное хранилище можно отключить целиком: `HSE_DISABLE_KEYCHAIN=true` (в окружении или `.env`) — для CI,
серверов без графической сессии и Linux без Secret Service. `npm test` задаёт её сам, поэтому тесты не
трогают настоящее хранилище.

Оговорки:

- Если `@napi-rs/keyring` не установился (`npm ci --omit=optional`, нет бинарника под платформу) или
  хранилище недоступно, проект молча работает дальше с `.env`, установка не падает.
- На Linux нужен запущенный Secret Service (GNOME Keyring, KWallet и т.п.; в сессии без графики часто
  его нет). Без него пакет ушёл бы в хранилище ядра `keyutils`, которое не переживает перезагрузку, поэтому
  в таком случае хранилище считается недоступным и остаётся `.env`. Эта проверка — синхронный вызов при
  старте; при заблокированной связке ключей или без D-Bus он может надолго задержать запуск, тогда задайте
  `HSE_DISABLE_KEYCHAIN=true`. Таймаута у вызова нет. Работа на Linux не проверялась.
- Защита рассчитана на случайную утечку и на чтение файлов другим пользователем системы, но не на
  вредоносную программу под вашим пользователем.

Если пароль или файлы каталога данных могли утечь: смените пароль ВШЭ (в `.mcpb` — и в настройках
расширения), удалите `hse-session.json` и `writes/key` в каталоге данных (папка `outbox/` и журнал
записей — ваши данные), выполните `npm run credentials:clear`. Подробности и контакт для сообщений об уязвимостях — в `SECURITY.md`.
Для публичного репозитория включите на GitHub secret scanning и push protection (по желанию — `gitleaks`
в pre-commit; в зависимости проекта он не входит).

## Тулы

21 тул чтения и 3 тула записи (только при `HSE_ENABLE_WRITES=true`). Что из этого покрыто тестами и
проверено вживую — в [`docs/status.md`](docs/status.md).

| Модуль | Тул | Описание |
|---|---|---|
| SmartLMS | `lms_list_courses` | Список курсов |
| SmartLMS | `lms_get_course_materials` | Элементы курса (файлы, задания, тесты и т.п.) |
| SmartLMS | `lms_list_assignments` | Задания курса со сроками |
| SmartLMS | `lms_get_grades` | Оценки по курсу: строки журнала с названием, путём категорий и оценкой; ошибка, если вёрстка журнала курса не распознана |
| SmartLMS | `lms_list_messages` | Входящие внутренней почты SmartLMS (переписка с преподавателями в курсах) |
| SmartLMS | `lms_get_message` | Полный текст письма внутренней почты SmartLMS с вложениями |
| SmartLMS | `lms_open_page` | Открыть произвольную страницу edu.hse.ru |
| ЕЛК | `elk_get_schedule` | Расписание через API ЕЛК — работает из любой сети |
| ЕЛК | `elk_get_user_info` | Данные аккаунта: ФИО, email, роли |
| ЕЛК | `elk_get_academic_data` | Академическая карточка: рейтинг, GPA, кредиты, программа, группа |
| ЕЛК | `elk_search_services` | Поиск по каталогу из 254+ сервисов ЕЛК (справки, заявки и т.п.) |
| ЕЛК | `elk_list_requests` | История поданных заявок (`/claim`) со статусами |
| ЕЛК | `elk_get_notifications` | Уведомления (колокольчик в шапке) |
| ЕЛК | `elk_get_section` | Раздел ЕЛК по имени: `profile` (страница профиля), `login_history` (история входов), `tasks` (задачи), `dpo_programs` (программы ДПО), `antiplagiat`, `course_evaluation` (СОП), `thesis` (ВКР/КР), `portfolio`, `internships` (практики), `scholarship_history` (повышенная стипендия) |
| ЕЛК | `elk_open_page` | Открыть произвольную страницу lk.hse.ru |
| Сводки | `my_today` | Что сегодня: занятия, дедлайны, непрочитанные письма LMS и уведомления ЕЛК, активные заявки |
| Сводки | `my_week` | План на 7 дней: расписание, дедлайны, экзамены, просроченные несданные работы |
| Сводки | `my_deadlines` | Что горит: несданные задания и тесты (в т.ч. просроченные), окна ЕЛК, экзамены за `days` дней |
| Изменения | `hse_changes` | Что изменилось с прошлого раза: дедлайны, расписание, входящее, заявки (и оценки LMS по явной просьбе); `ack` подтверждает полученное |
| Изменения | `hse_changes_reset` | Стереть локальные базы и журнал событий (`confirm: true`) |
| Служебное | `hse_clear_cache` | Сбросить кэш в памяти, чтобы следующий вызов взял свежие данные |
| SmartLMS (запись) | `lms_submit_assignment` | Сдать или поправить ответ на задание файлами из папки «исходящих». **Только при `HSE_ENABLE_WRITES=true`** |
| SmartLMS (запись) | `lms_delete_submission` | Удалить представленный ответ на задание. **Только при `HSE_ENABLE_WRITES=true`** |
| ЕЛК (запись) | `elk_order_certificate` | Заказать справку через форму сервиса ЕЛК (необратимо). **Только при `HSE_ENABLE_WRITES=true`** |

## Тулы записи — что нужно знать

Три тула записи (`lms_submit_assignment`, `lms_delete_submission`, `elk_order_certificate`) отправляют
данные в SmartLMS и ЕЛК, заказ справки в ЕЛК необратим, поэтому по умолчанию тулов записи у сервера нет вообще. Чтобы
включить, добавьте в `.env` строку `HSE_ENABLE_WRITES=true` (в Claude Desktop — галочка «Разрешить
отправку данных» в настройках расширения; значение читается при старте процесса, после правки
перезапустите сервер). Остальные тулы только читают.

- `lms_submit_assignment` — `cmid` (id модуля задания из `lms_get_course_materials`), `files`
  (имена файлов, не пути), `mode`: `draft` (по умолчанию) или `submit`. Файлы берутся только из
  папки «исходящих»: `outbox/` в каталоге данных или папка из `HSE_OUTBOX_DIR` (абсолютный путь или
  относительно корня проекта); если папки нет, а её родитель есть, она создаётся. Не больше 10 файлов,
  только обычные непустые файлы; папка не может быть корнем проекта или каталогом данных (кроме его
  `outbox/`). Файлы не удаляются и не перемещаются. Одноимённый
  файл ответа перезаписывается, остальные файлы ответа остаются. Онлайн-текст не поддерживается:
  параметр `text` отклоняется.
- `lms_delete_submission` — `cmid`. Удаляет ответ из системы; файлы в папке «исходящих» остаются.
- `elk_order_certificate` — `serviceId` (число `components` из `url` результата `elk_search_services`, не поле `id`) и `fields`
  (подпись поля формы → точный текст варианта выпадающего списка). Сухой прогон с пустым `fields`
  откроет форму и вернёт отказ со списком обязательных полей и вариантов (неподдерживаемые варианты
  помечены) — так узнают, что выбирать; превью и `previewToken` выдаются, когда все обязательные поля
  заданы. Значения, которые форма подставила сама из личных данных ЕЛК, показываются только в ответе
  сухого прогона и уйдут в заявку как есть.
  **Заказ необратим: отмены заявки в ЕЛК нет.** Заполняются только выпадающие списки; обязательное
  поле другого типа, которое форма не заполнила сама, и варианты вида «…в дополнительном поле»
  отклоняются. За раз выполняется один заказ. Исход проверяется по заявкам «Сервиса Студент» со
  страницы «Заявки» (в `elk_list_requests` их нет): `confirmed` — после заказа появилась ровно одна
  новая заявка, её номер в ответе; иначе `unknown`, и `newClaims: 0` не означает, что заказ не прошёл.
  При `unknown` сверяйте вручную на странице «Заявки» в lk.hse.ru и не повторяйте заказ без этой
  проверки. Новый источник проверки вживую с заказом ещё не проверялся.

Как пользоваться:

1. **Сначала сухой прогон.** Вызов без `confirm` ничего не отправляет: он читает страницу задания (для
   справки — форму) и показывает, что будет добавлено и заменено, предупреждения и `previewToken`.
2. **Запись** — тот же вызов с `confirm: true` и `previewToken` из сухого прогона. Токен действует
   15 минут, одноразовый и привязан к заданию, режиму, файлам (по содержимому) и состоянию ответа:
   если что-то изменилось, нужен новый сухой прогон.
3. **Не добавляйте тулы записи в автоматические разрешения клиента** (в Claude Desktop — не выбирайте
   для них «Разрешать всегда»). Защита от случайной отправки
   держится на запросе разрешения клиента MCP перед каждым вызовом: сервер не может проверить, что
   человек на месте.
4. **Исход проверяется чтением** и бывает трёх видов: `confirmed` (подтверждено), `rejected` (система
   отказала, состояние не изменилось), `unknown` (проверить не удалось). После `unknown` не повторяйте
   отправку: откройте задание на сайте (для справки — страницу «Заявки» в lk.hse.ru) и посмотрите, что там на
   самом деле. Повторный вызов с тем же `previewToken` ничего не отправляет: для заданий он перепроверит результат
   чтением, а для справки не перепроверяет, а возвращает `unknown` с подсказкой сверить вручную. Новая
   запись по этой цели потребует `acknowledgeUnknown: true`. Для заказа справки `rejected` не выносится
   никогда.
5. Сохранение может сразу отправить работу на оценку: у заданий без отдельного шага «Отправить на
   оценку» `draft` этого не исключает. Шаг «Отправить на оценку» тул сам не выполняет. Просроченные и
   закрытые задания тул отклоняет.

Журнал записей лежит в `writes/journal.json` каталога данных (90 дней, не более 200 записей) и содержит только
технические поля: без содержимого, имён файлов, названий и id курсов и заданий.

## Как это устроено

- **SmartLMS и ЕЛК** — общий Playwright-браузер (`src/auth/browser-session.ts`), который проходит
  единый SSO ВШЭ (Keycloak, `saml.hse.ru`, realm `hse`) и переиспользует сессию между вызовами
  (сохраняется в `hse-session.json` каталога данных). Браузер — скачанный Playwright Chromium,
  системный Edge или Chrome (`src/auth/browser-launch.ts`). ЕЛК даёт и JSON API с `Authorization: Bearer
  <access_token>` из localStorage (`elk_get_schedule`, `elk_get_user_info`, `elk_search_services`,
  `elk_list_requests`).
- **istudy.hse.ru** (зачётка) не поддерживается: тулы `istudy_*` удалены 2026-10-23. Вход проходит без MAX, но данные закрыты двухфакторным подтверждением через MAX (HTTP 412 на `GET /api/auth/two-factor/required` после клика по «Зачетная книжка»), а эндпоинты после 2FA неизвестны; обход 2FA не предусмотрен. Оценки по курсам — `lms_get_grades`.
- **Сводки** (`my_today`/`my_week`/`my_deadlines`) — агрегируют расписание ЕЛК,
  календарь Moodle, окна и уведомления ЕЛК в один JSON; статус каждого источника — в `sources`,
  отказ одного не роняет сводку. Холодный вызов после
  запуска сервера занимает ≈ 17 с (логин и открытие страниц), затем 3–4 с, повтор из кэша — мгновенно
  (замеры на одном аккаунте, 2026-10-14).
- **Изменения** (`hse_changes`) — «что изменилось», а не «что есть»: тул снимает дедлайны, расписание
  на 14 дней, входящее (письма LMS, уведомления ЕЛК) и заявки ЕЛК, сравнивает с прошлым снимком и
  возвращает только события (новое, изменено, пропало, перенесено, просрочено). Базы по источникам
  и журнал событий лежат в локальном `changes/state.json` каталога данных; сброс —
  `hse_changes_reset` (полный сброс удаляет и копии `state.json.corrupt-*`). Первый вызов по источнику только создаёт базу, без событий. Курсор двигается
  не сам, а параметром `ack`: передайте `ackSeq` из ответа в следующий вызов, иначе те же события
  вернутся снова (событие не теряется, даже если ответ не дошёл). Параметры: `scopes` (по умолчанию
  без оценок), `courses` (фильтр курсов для оценок: id или часть названия), `fresh` (перечитать
  источники, иначе принимаются данные не старше 60 с), `since` (журнал за период, например `"7d"`, вместо
  неподтверждённого; `since` сам курсор не двигает, `ack` в том же вызове двигает). Оценки (`scopes: ["grades"]`) берутся только из журнала LMS
  и дорогие: страница на курс, десятки секунд. Тул только читает
  системы ВШЭ. Проверено вживую на одном
  аккаунте (спека §18); события `added`/`changed`/`moved` — только имитацией правки базы.

## Границы применимости

- Селекторы и тексты Moodle (SmartLMS) и ЕЛК проверены на одном аккаунте и одном семестре; у других
  факультетов и курсов разметка может отличаться.
- Онлайн-текст в ответе на задание не поддерживается.
- Заданий с отдельным шагом «Отправить на оценку» и с классом статуса `submissiondraft` не наблюдалось:
  в таких случаях тулы записи отказывают, а не угадывают.
- Заказ справки проверен на форме одной справки.
- Правила ВШЭ об автоматизированном доступе к SmartLMS и ЕЛК не проверялись, пользователь отвечает за их
  соблюдение сам.

## Известные ограничения

- **Расписание в сводках — только из ЕЛК**: если ЕЛК не ответил, в `my_*` будет ошибка источника
  `elk_schedule` без занятий (запасного пути через RUZ больше нет, RUZ — в hse-ruz-mcp и только из
  сети ВШЭ).
- **ЕЛК частично разведан вживую (2026-09-15)**: известны разделы `/task`, `/claim` (теперь
  структурированно доступен через `elk_list_requests`), `/service` (поиск — `elk_search_services`),
  `/schedule`, `/student-services`, `/faq`. `/student-services?section=documents` содержит каталог
  заказа справок (зачётка, подтверждение обучения и т.п.) — форма разведана (Formily-конструктор
  поверх Element Plus); заказ идёт тулом `elk_order_certificate` (при `HSE_ENABLE_WRITES=true`).
  `elk_open_page` возвращает сырой текст + ссылки для остальных случаев. Подробные заметки разведки хранятся у автора локально и в репозиторий не входят.
- **`hse_changes`:** в ответе не более 200 событий (остаток — `omitted`: подтвердите `ackSeq` и
  вызовите снова); журнал хранит не более 500 событий, при переполнении невыданные теряются —
  счётчик `gap.lostEvents`. События считаются только в пересечении старого и нового окон (календарь
  −180…+60 дней, расписание 14 дней); при усечении календаря «пропало» не вычисляется. Виды событий
  по областям: дедлайны — новое, изменено, пропало, просрочено; расписание — новое, изменено,
  пропало, перенесено; входящее — только новое; заявки и оценки — новое и изменено (без «пропало»).
  «Пропало из календаря» не значит «сдано». «Итого за курс» — обычный элемент оценок: смена итога
  видна рядом с событием самого элемента.
- **ЕЛК: запись только заказом справки** (`elk_order_certificate`, `HSE_ENABLE_WRITES=true`); подача
  заявок (практика, стипендия, мобильность и т.п.) не реализована, остальные тулы ЕЛК только читают.
  Заказ справки выполнен вживую один раз на одном аккаунте: заказ дошёл, но исход тула — `unknown`
  (`newClaims: 0`), история заявок этих справок не видит; ручная проверка на странице «Заявки»
  подтвердила ровно одну новую заявку. Заказ необратим; свободный текст, даты, файлы
  и варианты «…в дополнительном поле» не поддерживаются (тул отказывает). Поля, которые форма
  дорисовывает после выбора варианта, сухой прогон не видит.
- **Запись в SmartLMS** (`HSE_ENABLE_WRITES=true`): только файлами, онлайн-текст не поддерживается. У
  заданий без отдельного шага «Отправить на оценку» сохранение сразу отправляет работу на оценку; сам
  шаг «Отправить на оценку» тул не выполняет. Просроченные и закрытые задания отклоняются, даже если
  поздняя сдача, возможно, ещё открыта. Задания с нераспознанным классом статуса сдачи отклоняются
  (статус «черновик» вживую не наблюдался). Существующий файл ответа и отправляемый файл крупнее 64 МиБ
  не принимаются: результат нельзя проверить. Проверено на одном аккаунте за один семестр.
- `lms_open_page` и `elk_open_page` отклоняют URL с параметром `sesskey` и страницы `/mod/assign/` с
  параметром `action`: они читают, но не выполняют действий.

## Разработка

- `npm run build` собирает проект в `dist/`, `npm run dev` — в режиме watch.
- `npm run lint` проверяет код линтером и форматтером [Biome](https://biomejs.dev), `npm run format`
  исправляет то, что можно исправить автоматически.
- `npm run pack:mcpb` собирает пакет Claude Desktop в `build/hse-mcp-<версия>.mcpb` (CI делает то же и
  прикрепляет пакет к релизу).
- `npm test` собирает проект и запускает тесты `node:test` (`test/*.test.mjs`): сеть, браузер и учётные
  данные им не нужны. Среди них архитектурный тест: импорты должны соблюдать слои.

Как устроен код и куда добавлять новое — [`docs/architecture.md`](docs/architecture.md), правила
участия — [CONTRIBUTING.md](CONTRIBUTING.md).

## Лицензия

[MIT](LICENSE)

TDQS

A3.5/5.0

Scored across 21 tools

Disambiguation4/5

Prefixes (elk_/lms_/my_/hse_) plus explicit cross-references in descriptions (e.g. elk_get_user_info says to use elk_get_academic_data for academic card; elk_get_section notes profile replacement) make most tools clearly separable. The main softness is the trio my_today / my_week / my_deadlines, whose windows overlap (today already includes deadlines through tomorrow, week includes deadlines too), and the two generic open_page escape hatches that can retrieve anything the specific tools return.

Naming Consistency4/5

Almost all tools are snake_case verb_noun with a source prefix (elk_get_schedule, lms_list_messages, elk_search_services), which is predictable and readable. The deviations are modest: the my_* aggregators use noun-only names (my_today, my_week, my_deadlines), and hse_changes / hse_changes_reset / hse_clear_cache break the source_action pattern with a prefix plus noun.

Tool Count3/5

21 tools is at the heavy end for what is essentially a read-only student dashboard, and there is visible redundancy: three overlapping summary tools, two generic open_page catch-alls, two snapshot/maintenance tools, and both elk_get_user_info and elk_get_academic_data. The scope (two separate systems plus cross-system aggregation) does justify breadth, so it is borderline rather than bloated.

Completeness3/5

Read coverage is broad — schedule, academic record, courses, materials, assignments, grades, mail, requests, notifications, plus aggregate digests and diffing — but every tool is read-only: no request/application submission (explicitly stated as unimplemented), no assignment submission, no message sending, no profile edits. The two open_page tools let an agent navigate uncovered read paths, but they too return only text and links, so write-side workflows remain dead ends.

Maintenance

ActivityNo data
ResponsivenessNo issues