mirror of
https://github.com/EDeev/mospolytech-mcp.git
synced 2026-10-07 20:49:52 +03:00
219 lines
13 KiB
Markdown
219 lines
13 KiB
Markdown
# Mospolytech MCP
|
||
|
||
Наш учебный проект по дисциплине «Автоматизация процессов жизненного цикла
|
||
программных средств» (Московский Политех, гр. 241–327). Делаем MCP-сервер,
|
||
который даёт LLM-агенту (например, Claude) доступ к данным университета —
|
||
и к открытому справочнику (группы, расписание), и к личному кабинету
|
||
конкретного авторизованного пользователя (расписание, оценки, уведомления,
|
||
платежи, заявки, сообщения).
|
||
|
||
Команда — три человека:
|
||
|
||
- Деев Егор Викторович
|
||
- Шмыговский Никита Сергеевич
|
||
- Старков Руслан Владимирович
|
||
|
||
Официальное техническое задание, которое сдавали на лаб. №1 —
|
||
[`docs/official/lab01-tech-spec.pdf`](docs/official/lab01-tech-spec.pdf).
|
||
Наш рабочий план по всем 8 лабораторным (архитектура, черновая схема БД,
|
||
открытые риски) — [`docs/working/plan.md`](docs/working/plan.md). Подробнее
|
||
про то, как мы делим документацию на официальную и рабочую — в
|
||
[`docs/README.md`](docs/README.md).
|
||
|
||
## Что уже есть
|
||
|
||
Мы собрали обе половины университетского API в этот репозиторий и
|
||
объединили их в один объект `UniversityAPI`, чтобы MCP-серверу (и нам
|
||
самим) не приходилось таскать два клиента порознь:
|
||
|
||
```python
|
||
from mospolytech_mcp import UniversityAPI
|
||
|
||
async with UniversityAPI() as uni:
|
||
groups = await uni.open.get_groups() # открытые данные, без авторизации
|
||
await uni.lk.login(login, password) # а тут уже личный кабинет
|
||
my_schedule = await uni.lk.get_my_schedule()
|
||
```
|
||
|
||
- **`open_api`** — открытые справочные данные университета. По ТЗ (ТПО-004)
|
||
здесь используется готовая библиотека
|
||
[mospolytech_api](https://github.com/r4nd0lph-c/mospolytech_api), а не
|
||
наша реализация — мы завендорили её код как есть в `open_api/_vendor/` и
|
||
сверху накрутили тонкую async-обёртку (`asyncio.to_thread`), чтобы она не
|
||
блокировала event loop рядом с остальным асинхронным кодом.
|
||
- **`lk_api`** — личный кабинет. Тут по ТЗ (ТПО-005) наоборот: логику
|
||
авторизации и работы с API переносим с существующей Go-библиотеки
|
||
[MPU_LK_API](https://github.com/GODIMONGO/MPU_LK_API) на Python, своими
|
||
руками. Сделали: логин/логаут/обновление токенов (обе системы — легаси
|
||
token и JWT+refresh), расписание, успеваемость, платежи и договоры,
|
||
заявки, уведомления и объявления, диалоги и (осторожно,
|
||
неподтверждено — см. `docs/working/plan.md`) отправку сообщений.
|
||
|
||
Сам MCP-сервер (`server.py`) уже запускается и отвечает на первый
|
||
инструмент — `list_groups` (ФОД-001), через полный стек: протокол MCP →
|
||
`UniversityAPI.open` → кэш в PostgreSQL (`cache.py` + `db/`, миграции
|
||
Alembic в `alembic/`). Живьём проверяли: 592 группы забрались с
|
||
rasp.dmami.ru, записались в БД, второй вызов уже идёт из кэша. Остальные
|
||
инструменты (ФЛК-*, оставшиеся ФХД) дописываем по плану в
|
||
`docs/working/plan.md`, раздел 3.1.
|
||
|
||
## Требования
|
||
|
||
- Python 3.14 (у нас работает через `py -3.14`)
|
||
- Доступ к PostgreSQL — свой пользователь `mospolytech_mcp` на общем сервере
|
||
команды (не `postgres`/admin), спросите данные в чате, если не сохранили
|
||
|
||
## Быстрый старт
|
||
|
||
Если стоит GNU Make (на Windows — `choco install make`), всё поднимается
|
||
одной командой:
|
||
|
||
```powershell
|
||
make up
|
||
```
|
||
|
||
Она создаёт `.venv`, ставит зависимости, копирует `.env.example` в `.env`,
|
||
накатывает миграции и запускает сервер. На свежем клоне первый запуск
|
||
остановится с просьбой вписать `DATABASE_URL` в `.env` — впишите и
|
||
запустите `make up` ещё раз. Остальные команды (`make test`, `make migrate`,
|
||
`make run`, `make check-lk`, `make clean`) — в `make help`. Работает из
|
||
PowerShell, cmd и Git Bash, а также на Linux/macOS.
|
||
|
||
Ниже — то же самое руками, без make.
|
||
|
||
## Установка
|
||
|
||
```powershell
|
||
py -3.14 -m venv .venv
|
||
.venv\Scripts\pip install -e ".[dev]"
|
||
copy .env.example .env
|
||
# впишите DATABASE_URL (и MPU_LOGIN/MPU_PASSWORD, если нужен check_lk_api.py)
|
||
.venv\Scripts\alembic upgrade head
|
||
```
|
||
|
||
## Тесты
|
||
|
||
Всё, что не требует реального логина, покрыто офлайн-тестами — сеть не
|
||
трогаем, HTTP подменяем (`httpx.MockTransport` для `lk_api`, подмена
|
||
`requests.get` для `open_api`):
|
||
|
||
```powershell
|
||
.venv\Scripts\python -m pytest -q
|
||
```
|
||
|
||
## Проверка на реальном аккаунте
|
||
|
||
`open_api` не требует авторизации, поэтому его можно проверить вживую хоть
|
||
прямо сейчас (реальный запрос к rasp.dmami.ru, без секретов):
|
||
|
||
```powershell
|
||
.venv\Scripts\python -c "import asyncio; from mospolytech_mcp.open_api import OpenDataClient; asyncio.run(OpenDataClient().get_groups())"
|
||
```
|
||
|
||
Для `lk_api` нужен свой логин/пароль от e.mospolytech.ru — впишите
|
||
MPU_LOGIN и MPU_PASSWORD в свой `.env`, дальше `scripts/check_lk_api.py`
|
||
логинится и читает профиль, расписание, успеваемость, уведомления,
|
||
платежи, заявки и диалоги (ничего не меняет):
|
||
|
||
```powershell
|
||
.venv\Scripts\python scripts\check_lk_api.py
|
||
```
|
||
|
||
Пароль используется один раз при вызове `login()` и нигде не сохраняется.
|
||
`.env` в `.gitignore`, в репозиторий не попадёт.
|
||
|
||
## Запуск сервера
|
||
|
||
```powershell
|
||
.venv\Scripts\python -m mospolytech_mcp.server
|
||
```
|
||
|
||
Поднимается на stdio-транспорте — так подключается локальный MCP-клиент
|
||
(например, Claude Desktop/Code). Инструменты:
|
||
|
||
| Инструмент | Что делает |
|
||
|---|---|
|
||
| `list_groups` | список групп университета (ФОД-001), кэш в БД на 15 минут |
|
||
| `add_tracked_group(user, group)` | добавить группу в отслеживаемые пользователя; группа сверяется со списком `list_groups`, повторное добавление ничего не дублирует |
|
||
| `list_tracked_groups(user)` | отслеживаемые группы пользователя в порядке добавления |
|
||
| `remove_tracked_group(user, group)` | убрать группу из отслеживаемых |
|
||
|
||
Отслеживаемые группы лежат в таблице `tracked_groups`. Авторизации на
|
||
сервере пока нет, так что `user` — просто логин, который передаёт клиент.
|
||
|
||
### HTTP-режим
|
||
|
||
Если сервер крутится в VM или на другой машине, stdio не подходит —
|
||
есть HTTP-режим (streamable HTTP, эндпоинт `/mcp`):
|
||
|
||
```bash
|
||
make run-http # 0.0.0.0:8000
|
||
make run-http PORT=9000 # HOST и PORT можно переопределить
|
||
```
|
||
|
||
Без make: `python -m mospolytech_mcp.server --http --host 0.0.0.0 --port 8000`.
|
||
|
||
В Postman: New → MCP, транспорт HTTP, URL `http://<адрес VM>:8000/mcp`,
|
||
Connect — в списке инструментов появится `list_groups`, его можно вызвать
|
||
прямо оттуда. Авторизации на сервере нет, так что наружу (`0.0.0.0`)
|
||
открывать только на тестовой машине.
|
||
|
||
## Docker
|
||
|
||
Тот же сервер, но в контейнере и со своей PostgreSQL рядом — так он живёт
|
||
на ВМ начиная с ЛР3. Нужны Docker и docker compose v2, `.env` и venv не
|
||
нужны:
|
||
|
||
```bash
|
||
make docker-up
|
||
make docker-logs
|
||
make docker-down
|
||
```
|
||
|
||
Что происходит:
|
||
|
||
- `Dockerfile` собирает образ из исходников: `python:3.14-slim`, отдельным
|
||
слоем зависимости из `pyproject.toml`, потом сам пакет. При старте
|
||
контейнер накатывает миграции (`alembic upgrade head`) и запускает сервер
|
||
в HTTP-режиме на `0.0.0.0:8000` — снаружи это `http://<адрес>:8000/mcp`.
|
||
- `docker-compose.yaml` поднимает два сервиса: `db` (`postgres:17-alpine`)
|
||
и `mcp` (наш образ). Сервер стартует только после того, как БД ответила на
|
||
`pg_isready`, и ходит к ней по имени `db` внутри сети compose — наружу
|
||
порт PostgreSQL не выставлен. `DATABASE_URL` для контейнера compose
|
||
собирает сам из `POSTGRES_*`; `.env` в образ не попадает (`.dockerignore`).
|
||
- Файлы БД лежат в volume `pgdata`, поэтому `docker compose down` и
|
||
повторный `up` данные не теряют. Снести вместе с данными —
|
||
`docker compose down -v`.
|
||
|
||
Логин, пароль и имя БД по умолчанию `mospolytech_mcp`; переопределяются через
|
||
`POSTGRES_USER`/`POSTGRES_PASSWORD`/`POSTGRES_DB` в `.env` — compose читает
|
||
его сам, а `DATABASE_URL` оттуда для контейнера не используется.
|
||
|
||
Посмотреть, что данные реально легли в контейнерную БД (это же удобно
|
||
показывать после рестарта):
|
||
|
||
```bash
|
||
docker compose exec db psql -U mospolytech_mcp -c "select id, jsonb_array_length(groups), fetched_at from groups_cache"
|
||
docker compose exec db psql -U mospolytech_mcp -c "select * from tracked_groups"
|
||
```
|
||
|
||
## Структура репозитория
|
||
|
||
```
|
||
docs/
|
||
official/ — официальные документы для сдачи (ТЗ и т.д.), PDF, не правим задним числом
|
||
working/ — наш рабочий план, заметки, черновики — правим постоянно
|
||
sdo/ — методички по всем 8 лабораторным и справочные ГОСТ/IEEE из СДО
|
||
src/mospolytech_mcp/
|
||
api.py — UniversityAPI, точка входа: держит open_api + lk_api вместе
|
||
server.py — сам MCP-сервер (регистрация tools)
|
||
cache.py — кэширующие обёртки над UniversityAPI, пишут в БД (ФХД-003)
|
||
open_api/ — открытые данные (вендор mospolytech_api + наша async-обёртка)
|
||
lk_api/ — личный кабинет (наш порт MPU_LK_API с Go на Python)
|
||
db/ — модели SQLAlchemy (схема версионируется в alembic/)
|
||
alembic/ — миграции БД (alembic upgrade head накатывает схему)
|
||
tests/ — офлайн-тесты (pytest, без сети)
|
||
scripts/ — ручные проверочные скрипты (против реального ЛК)
|
||
Dockerfile — образ сервера (сборка из исходников, миграции + HTTP-режим на старте)
|
||
docker-compose.yaml — сервер + PostgreSQL с volume для данных
|
||
```
|