ot5-mcp-server
by Epyur
README.md
# MCP-сервер распознавания документов
MCP-сервер на TypeScript/Node.js для агентов в IDE (VSCode). Предоставляет 4 инструмента:
распознавание электронных PDF, Word (DOCX), Excel (XLSX) и поиск по PostgreSQL. Каждый инструмент возвращает агенту структурированный JSON.
## Возможности
| Инструмент | Что делает | Что возвращает |
|---|---|---|
| `extract_pdf` | Распознавание текстового (не сканированного) PDF | Метаданные, число страниц, текст постранично |
| `extract_word` | Распознавание DOCX | Заголовки, абзацы, таблицы, списки |
| `extract_excel` | Распознавание XLSX | Листы, колонки, число строк, первые строки |
| `postgres_search` | Поиск по PostgreSQL (read-only) | Таблицы, колонки, строки (SELECT) |
Контракт результата каждого инструмента: см. [docs/contract.md](docs/contract.md).
## Принципы MCP
Агент (IDE) подключается к MCP-серверу по транспорту **stdio**: IDE запускает сервер как дочерний
процесс (в нашем случае — Docker-контейнер, см. `opencode.json`) и обменивается с ним сообщениями
JSON-RPC 2.0. Жизненный цикл подключения состоит из трёх фаз: `initialize` → `tools/list` → `tools/call`.
На фазе `tools/list` агент получает описания инструментов (имя, описание, схему входных параметров) и
добавляет их в контекст модели; на фазе `tools/call` агент передаёт серверу аргументы, сервер выполняет
реальную работу и возвращает структурированный JSON-результат, который попадает обратно в контекст
модели для формирования ответа.
**Tool** — это функция, объявленная сервером: у неё есть имя, человекочитаемое описание и JSON-схема
параметров. Модель сама ничего не выполняет — она лишь решает, какой tool вызвать и с какими аргументами;
исполнение всегда происходит на стороне MCP-сервера. В этом проекте тулами являются `extract_pdf`,
`extract_word`, `extract_excel` и `postgres_search`. Наглядное объяснение этой схемы со схемами Mermaid —
в [docs/mcp-explained.html](docs/mcp-explained.html).
## Требования
- Node.js 20.11+ (используется `import.meta.dirname`)
- PostgreSQL (только для тула `postgres_search`)
## Установка и запуск
```bash
npm install # установка зависимостей
npm run build # сборка в dist/
npm run make-samples # сгенерировать образцы в samples/ (для проверки)
npm start # запуск сервера напрямую (stdio)
```
Переменные окружения — в файле `.env` (скопируйте `.env.example`, укажите `DATABASE_URL`). Реальный `.env` не коммитится.
## Запуск в Docker
Всё окружение поднимается контейнерами: MCP-сервер (сборка из `Dockerfile`) и PostgreSQL с тестовыми данными.
```bash
# 1. Собрать образ MCP-сервера
docker build -t ot5-mcp-server .
# 2. Поднять PostgreSQL с тестовыми данными (db/init.sql)
docker compose up -d db
# 3. Проверка (опционально): тулы через stdio-контейнер
docker run -i --rm --network ot5_default -e PROJECT_ROOT=/project \
-e DATABASE_URL=postgres://dev:dev@db:5432/docs \
-v "%CD%:/project" ot5-mcp-server:latest
```
Схема: `db` живёт в сети `ot5_default`; MCP-контейнер VSCode подключается к той же сети и ходит к БД по имени сервиса `db`. Данные Postgres в named volume `pgdata`.
## Подключение к агенту в VSCode (opencode)
> В проекте используется расширение **opencode** для VSCode (`sst-dev.opencode`).
> opencode подключает MCP-серверы через свой конфиг `opencode.json` (а не через `.vscode/mcp.json`,
> который нужен только для встроенного MCP-шлюза GitHub Copilot).
1. Установите зависимости и соберите проект: `npm install && npm run build`.
2. Поднимите окружение в Docker:
```bash
docker compose up -d db
docker build -t ot5-mcp-server .
```
3. В корне проекта уже лежит `opencode.json` — он запускает `docs-server` как контейнер:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"docs-server": {
"type": "local",
"command": [
"C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe",
"run", "-i", "--rm", "--network", "ot5_default",
"-e", "PROJECT_ROOT=/project",
"-e", "DATABASE_URL=postgres://dev:dev@db:5432/docs",
"-v", "C:\\Users\\User\\Documents\\HW\\OT-5:/project",
"ot5-mcp-server:latest"
],
"enabled": true
}
}
}
```
> Docker должен быть запущен, образ `ot5-mcp-server:latest` собран, сеть `ot5_default` создана.
> Путь к `docker.exe` — полный, т.к. Docker не в PATH.
4. **Перезапустите opencode** (закройте/откройте окно VSCode или перезапустите сессию агента) — конфиг читается при старте.
5. В чате агента отправьте запрос, явно называющий инструмент, например: «Вызови MCP-инструмент `extract_pdf` для `samples/sample.pdf`».
6. Подтверждение вызова: ответ агента придёт как JSON, а логи сервера появятся в терминале/Docker.
> Секреты: строка БД для docker-режима — локальная dev-учётка `dev:dev`, только для тестов.
## Проверка без IDE (смоук-тест)
```bash
npm run smoke-test
```
Скрипт `scripts/smoke-test.mjs` поднимает собранный сервер по stdio через MCP-клиент и вызывает все тулы.
Вывод последнего прогона: [docs/evidence/smoke-test.log](docs/evidence/smoke-test.log).
Пример строки лога на стороне сервера (имя тула, параметры, статус):
```json
{"ts":"2026-08-20T06:45:44.748Z","tool":"extract_pdf","params":{"path":"samples/sample.pdf"},"status":"success"}
{"ts":"2026-08-20T06:45:44.787Z","tool":"extract_word","params":{"path":"samples/sample.docx"},"status":"success"}
{"ts":"2026-08-20T06:45:44.798Z","tool":"extract_excel","params":{"path":"samples/sample.xlsx"},"status":"success"}
{"ts":"2026-08-20T06:45:44.811Z","tool":"postgres_search","params":{"operation":"list_tables"},"status":"success"}
```
Логирование реализовано в `src/logger.ts:20–34` (вычищает ключи вида `password`/`token`).
## Безопасность и ограничения
- **Доступ к файлам** — только относительные пути внутри корня проекта; обход через `../` запрещён
(`src/security.ts:6–22`).
- **PostgreSQL** — только чтение: сессия `BEGIN READ ONLY`, только `SELECT`, без мультистейтментов,
таймаут запроса 10 с (`src/tools/postgres.ts:43–86`). Строка подключения — только из `.env`, в логи не попадает.
- **Секреты** — в репозитории только `.env.example`; логирование вычищает ключи вида `password`/`token` и т.п. (`src/logger.ts:20–34`).
- **PDF** — только электронные (текстовые) PDF. Сканированные документы (изображения) не распознаются — OCR не входит в объём.
## Ссылки на код (по требованиям задания)
1. **Сервер и регистрация инструментов** — `src/index.ts:35–106` (тулы) и `src/index.ts:107–108` (stdio-транспорт).
2. **Реализация инструментов:**
- `extract_pdf` — `src/tools/pdf.ts:14–33` (реализация), логирование в `src/index.ts:36–49`;
- `extract_word` — `src/tools/word.ts:17–71`, логирование в `src/index.ts:52–65`;
- `extract_excel` — `src/tools/excel.ts:15–36`, логирование в `src/index.ts:68–81`;
- `postgres_search` — `src/tools/postgres.ts:43–86`, логирование в `src/index.ts:84–104`.
3. **Логирование вызовов** — `src/logger.ts:20–34`; пример вывода: [docs/evidence/smoke-test.log](docs/evidence/smoke-test.log).
4. **Контракт результата** — [docs/contract.md](docs/contract.md).
## Проверочные запросы к агенту (критерий «вызовы из IDE»)
Запросы выполнялись в чате агента opencode внутри VSCode. Транскрипт диалога: `mcp_ans.md` (не коммитится,
содержит извлечённый контент личных документов). Сводная таблица: [docs/evidence/verification.md](docs/evidence/verification.md).
| # | Запрос в VSCode | Ожидаемый tool | Факт (по транскрипту) |
|---|---|---|---|
| 1 | «Какие MCP тебе доступны» | — (проверка конфигурации) | Агент прочитал `opencode.json`, перечислил 4 тула docs-server |
| 2 | «Распознай все PDF-файлы в папке» | `extract_pdf` ×2 | Вызван для `Чек 3 743.pdf` и `samples/sample.pdf` — текст извлечён |
| 3 | «Дай резюме по файлу Анализ…МЧС России.docx» | `extract_word` | Резюме документа сформировано по извлечённому тексту |
| 4 | «Покажи список таблиц в БД» | `postgres_search` (list_tables) | Возвращены employees, orders, products |
| 5 | «Покажи список таблиц в БД» (повторно) | `postgres_search` (list_tables) | Аналогичный результат |
| 6 | «Выжимка по стоимости из Перечень…xls» | `extract_excel` | Сформирована таблица с ценами и сроками изготовления |
| 7 | «Ревью документа Приложение 0…pdf» | `extract_pdf` (негативный) | Корректная ошибка «файл не найден в проекте» |
| 8 | «Прочитай файл Приложение.pdf в C:\Users\User\Documents\» | `extract_pdf` (негативный) | Ошибка: доступ только к папке проекта через Docker volume; Read отклонён пользователем |
Итог по критерию: **8 проверочных запросов, из них 7 приводят к вызову MCP-тула** (требование «≥5 запросов,
≥3 реальных вызова» выполнено с запасом), плюс 2 негативных запроса подтверждают границы безопасности.
## Структура проекта
```
src/index.ts # сервер, stdio-транспорт, регистрация тулов
src/logger.ts # логирование вызовов (имя, параметры, статус)
src/security.ts # проверка путей внутри корня проекта
src/tools/pdf.ts # PDF (pdf-parse)
src/tools/word.ts # DOCX (mammoth + cheerio)
src/tools/excel.ts # XLSX (xlsx / SheetJS)
src/tools/postgres.ts # PostgreSQL (pg, read-only)
scripts/make-samples.ts # генерация образцов
scripts/smoke-test.mjs # смоук-тест через MCP-клиент
Dockerfile # образ MCP-сервера
docker-compose.yml # PostgreSQL с тестовыми данными
db/init.sql # инициализация БД (таблицы + данные)
opencode.json # MCP-конфиг для агента opencode
docs/contract.md # контракт результатов
docs/evidence/ # логи подтверждений (smoke-test.log, verification.md)
docs/mcp-explained.html # наглядное объяснение принципов MCP (схемы Mermaid)
```This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues