Media Library MCP
by SergeyLT
README.md
# Media Library MCP
MCP-сервер и Telegram-бот для ведения личной медиатеки: фильмов, сериалов, анимации, книг и игр.
[](https://github.com/SergeyLT/mcp-mediateka/actions/workflows/ci.yml)
## О проекте
Media Library MCP хранит записи медиатеки в SQLite и предоставляет их через [Model Context Protocol](https://modelcontextprotocol.io/). Telegram-бот принимает запрос на естественном языке, передаёт модели только актуальные схемы MCP-инструментов и возвращает её ответ пользователю.
- MCP-сервер отделён от интерфейса: к одной медиатеке могут подключаться Telegram-бот и другие совместимые клиенты.
- Данные хранятся в локальном SQLite-файле; Docker Compose сохраняет его в именованном volume.
- Вызовы инструментов описаны строгими JSON-схемами и валидируются до работы с базой.
- Логи выводятся в stdout строками JSON и не содержат текста сообщений, ключей и иных секретов.
- Проверки не обращаются к Telegram или OpenAI: модель заменяется фейком, а
интеграционный тест поднимает MCP-сервер только на локальном loopback-порту.
## Возможности
- Добавление фильмов, сериалов, анимации, книг и игр.
- Поиск по названию, автору/создателю и жанру с частичным совпадением.
- Фильтрация по типу, статусу, жанру, году выпуска и личной оценке.
- Сопоставление распространённых русских и английских названий жанров, например
«фантастика» / `science fiction` / `sci-fi`.
- Редактирование и удаление записей, включая очистку дублей.
- Подтверждение перед добавлением записи с похожим названием.
- Статистика коллекции.
- Диалог с Telegram-ботом на естественном языке через OpenAI Responses API и MCP-инструменты.
- Набор из 100 демо-записей для первого запуска.
## Стек
| Задача | Технология |
| --- | --- |
| MCP-сервер | MCP Python SDK, Streamable HTTP |
| Telegram-интерфейс | aiogram |
| Модель и tool calling | OpenAI Responses API |
| Хранилище | SQLite, SQLAlchemy async, aiosqlite |
| Конфигурация | pydantic-settings |
| Контейнеры | Docker, Docker Compose |
| Проверки | pytest, Ruff, mypy |
## Архитектура
```text
.
├── mcp_server/
│ ├── server.py # запуск Streamable HTTP MCP-сервера
│ ├── tools.py # MCP-инструменты и их схемы
│ ├── db.py # async-репозиторий SQLite
│ ├── models.py # ORM-модель и перечисления предметной области
│ ├── schemas.py # входные и выходные Pydantic-схемы
│ ├── seed.py # 100 детерминированных демо-записей
├── telegram_bot/
│ ├── bot.py # запуск long polling
│ ├── handlers.py # Telegram-команды и текстовый диалог
│ ├── agent.py # цикл OpenAI Responses API ↔ MCP-инструменты
│ ├── mcp_client.py # Streamable HTTP MCP-клиент
│ └── config.py # конфигурация Telegram-бота
├── shared/logging.py # JSON-логирование stdout
├── tests/ # unit- и локальные HTTP-интеграционные тесты
├── docs/
│ └── manual-test-scenarios.md # сценарии ручной проверки демо-базы
├── Dockerfile.mcp # образ MCP-сервера
├── Dockerfile.bot # образ Telegram-бота
└── docker-compose.yml # совместный локальный запуск
```
Бот не обращается к базе напрямую: он получает список инструментов у MCP-сервера, модель выбирает нужный из них, а клиент выполняет вызов через Streamable HTTP. Это оставляет работу с данными в одном слое и позволяет использовать MCP-сервер независимо от Telegram.
## Быстрый старт
Нужны Python 3.12+, [uv](https://docs.astral.sh/uv/) и Docker Desktop для варианта с контейнерами.
Скопируйте шаблон настроек:
```powershell
Copy-Item .env.example .env
```
Заполните в `.env` как минимум:
```dotenv
TELEGRAM_BOT_TOKEN=токен_из_BotFather
ALLOWED_USER_IDS=ваш_telegram_user_id
OPENAI_API_KEY=ваш_ключ_OpenAI
```
`OPENAI_MODEL` задаёт модель, а `MAX_TOOL_CALLS` ограничивает число MCP-вызовов для одного сообщения. Не добавляйте `.env` в репозиторий.
### Локальный запуск без Docker
```powershell
uv sync --all-extras
uv run media-mcp-server
```
Во втором терминале:
```powershell
uv run media-library-bot
```
По умолчанию сервер доступен по `http://127.0.0.1:8000/mcp`, поэтому для запуска без Docker установите в `.env`:
```dotenv
MCP_SERVER_URL=http://127.0.0.1:8000/mcp
```
### Docker Compose
```powershell
docker compose up --build
```
Compose передаёт боту внутренний адрес `http://mcp-server:8000/mcp` автоматически. База медиатеки остаётся в именованном volume `media_data` после пересоздания контейнеров. Чтобы остановить сервисы, используйте `docker compose down`; эта команда не удаляет volume.
MCP-сервер публикует порт только на loopback-интерфейсе. Его endpoint готовности
`http://127.0.0.1:8000/health` используется Docker healthcheck; Telegram-бот
запускается только после успешной проверки MCP-сервера.
## Использование Telegram-бота
После `/start` можно писать обычные запросы, например:
```text
Добавь фильм «Прибытие» в планы, режиссёр Дени Вильнёв, жанр фантастика.
```
```text
Какие работы типа animation со статусом completed я оценил на 9 или 10?
```
```text
Покажи статистику моей медиатеки.
```
```text
Покажи завершённые фильмы жанра фантастика после 2000 года с оценкой от 8.
```
Если при добавлении найдётся похожее название, бот покажет существующие записи.
`/confirm` добавит новую запись намеренно, `/cancel` отменит операцию. Похожесть
определяется по нормализованному названию, поэтому окончательное решение всегда
остаётся за пользователем.
Команды:
- `/tools` — показать доступные MCP-инструменты;
- `/reset` — очистить короткую историю текущего диалога;
- `/confirm` — подтвердить добавление похожей записи;
- `/cancel` — отменить ожидающее добавление;
- `/help` — показать подсказку.
## Использование MCP-сервера
Streamable HTTP endpoint: `http://<host>:8000/mcp`.
Ниже пример минимального клиента на Python, который выводит статистику:
```python
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
async def main() -> None:
async with streamable_http_client("http://127.0.0.1:8000/mcp") as streams:
read_stream, write_stream, _ = streams
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
result = await session.call_tool("get_media_stats", {})
print(result.structuredContent)
asyncio.run(main())
```
Сервер предоставляет инструменты `list_media`, `search_media`, `get_media`,
`add_media`, `find_similar_media`, `update_media`, `delete_media`,
`update_media_status`, `rate_media` и `get_media_stats`. Их актуальные входные
JSON-схемы возвращаются стандартным запросом MCP `tools/list`.
Для ручной проверки Telegram-бота, фильтров, защиты от дублей и healthcheck
используйте [сценарии тестирования](docs/manual-test-scenarios.md).
## Тестирование
```powershell
uv run ruff format .
uv run ruff check . --fix
uv run mypy mcp_server telegram_bot shared
uv run pytest
```
Тесты проверяют создание и повторный seed базы, поиск с Unicode и частичным
совпадением, фильтры, изменение и удаление записей, обнаружение дублей,
регистрацию MCP-инструментов, отказ транспорта, лимит tool calls и цикл
«модель → инструмент → итоговый ответ». Отдельный интеграционный тест
поднимает локальный Streamable HTTP MCP-сервер, вызывает его настоящим
MCP-клиентом и проверяет healthcheck и отказ невалидных аргументов.
GitHub Actions запускает эти проверки и собирает Docker Compose при каждом push и pull request.
## Возможные улучшения
- Поддержка обложек, вложений и ссылок на внешние сервисы.
- Импорт и экспорт коллекции в JSON или CSV.
- Полнотекстовый поиск и персональные рекомендации.
- Авторизация нескольких пользователей и изоляция их коллекций.
- Кэширование схем MCP-инструментов и пул долгоживущих соединений.
- Наблюдаемость: метрики, трассировка и централизованное хранение логов.
## Лицензия
[MIT](LICENSE)
## Автор
[SergeyLT](https://github.com/SergeyLT)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues