Initial commit: omnichannel-mcp — MCP-сервер управления платформой Omnichannel
ci / test (push) Successful in 2m22s
ci / lint (push) Successful in 4m31s

This commit is contained in:
Maksim Totmin
2026-10-07 20:13:23 +07:00
commit fdbd4a4fb6
54 changed files with 5672 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
# Совместимость с версиями API
`omnichannel-mcp` рассчитан на API **`config_server` 1.1.0** (он же `latest`).
Эта версия добавила JSON-эндпоинты, на которые опирается сервер.
## Что появилось в 1.1.0
| Эндпоинт | Назначение |
|---|---|
| `POST /api/login`, `POST /api/logout` | JSON-авторизация для сторонних клиентов |
| `GET /api/whoami` | Проверка сессии |
| `GET /api/application/<id>` | Детализация сервиса в JSON |
| `POST /api/compose/<id>` | Установка compose через JSON |
| `POST /api/env/<id>/<file>` | Установка env через JSON |
| `GET /api/tasks`, `GET /api/task/<id>` | Список/статус задач |
В более старых версиях (≤ 1.0.20) этих эндпоинтов нет: логин был только
HTML-формой, конфиг правился HTML-страницами, а список задач — только по хосту.
`update_front` появился раньше (1.0.19+).
## Capability-probe
Инструмент `server_info` определяет версию автоматически (по доступности
`GET /api/whoami`) и возвращает:
```json
{
"server": "prod",
"base_url": "http://10.20.30.40:5005",
"read_only": false,
"capabilities": {
"version": "1.1.0",
"features": { "api_login": true, "set_compose": true, "tasks_filter": true, … }
}
}
```
На старом стенде `version` будет `legacy (<1.1.0)`, а `features` — пустым. В этом
случае инструменты, требующие новых эндпоинтов, вернут понятное сообщение вместо
«тихой» поломки. Наблюдение и часть операций на старых версиях недоступны —
обновите `config_server`.
## Рекомендация
Перед автоматизацией вызовите `server_info` и убедитесь, что `version` = `1.1.0`.
Если планируется смешанный парк — запускайте отдельный инстанс модуля на каждый
контур со своим `server`-конфигом.
+74
View File
@@ -0,0 +1,74 @@
# Архитектура
## Место в системе
```
┌────────────────┐ MCP (stdio, JSON-RPC) ┌──────────────────┐
│ MCP-клиент │ ◄──────────────────────► │ omnichannel-mcp │
│ (AI-ассистент)│ │ (этот сервер) │
└────────────────┘ └────────┬─────────┘
│ HTTP (session)
▼
┌──────────────────┐
│ config_server │
│ (Flask API/UI) │
└────────┬─────────┘
│ задачи агентам
▼
┌───────────────────────────┐
│ агенты на хостах │
│ docker-compose / swarm │
└───────────────────────────┘
```
`omnichannel-mcp` — тонкий клиент. Он не выполняет команды сам: создаёт задачи
через API `config_server`, а те забирают агенты нод. Сервер без состояния — при
перезапуске ничего не теряется.
## Модель задач
Действия жизненного цикла (`deploy`, `restart`, `down`, `migrate`,
`update_front`, релизные операции) асинхронны:
1. Инструмент вызывает API → создаётся задача → возвращается `task_id`.
2. Агент ноды забирает задачу, выполняет `docker-compose`, публикует результат.
3. Клиент опрашивает `get_task` до `completed`/`failed`.
Поэтому у инструментов есть параметр `wait`: по умолчанию он выключен (loop
ассистента последовательный — не стоит его блокировать), а при `wait=true`
ожидание ограничено `task_poll_max_sec` и не считается ошибкой, если время вышло:
вернётся текущее состояние задачи.
## Слои модуля
```
main.go каркас lifecycle: --health, -config, stdio
internal/configserver/ домен: без зависимостей от MCP
config.go разбор/валидация конфига, ${VAR}, merge local
session.go HTTP-клиент: cookie-сессия, релогin, ретраи GET
manager.go потокобезопасный диспетчер: конфиг, сессии, семафоры
api_*.go типизированные вызовы эндпоинтов config_server
internal/tools/ MCP-слой: разбор аргументов → вызов домена → текст
registry.go toolkit.go patterns.go util.go
observe.go configure.go operate.go release.go maintain.go overview.go
```
Правило разделения: домен тестируется без MCP; MCP-слой не содержит бизнес-логики.
## Надёжность
- **Сессия:** Flask-cookie хранится в cookie-jar; login ленивый, при `401`
выполняется один повторный вход и один повтор запроса.
- **Повторы:** только идемпотентные GET (сеть/5xx). Мутации не повторяются.
- **Ограничения:** context-deadline на каждый вызов, лимит на размер ответа,
обрезка вывода, семафор мутаций на сервер.
- **Ошибки:** доменные (4xx/5xx с текстом, ошибки валидации) возвращаются как
recoverable — ассистент видит причину и может исправить; инфраструктурные
(сеть) — как ошибка протокола.
- **Логи:** только в stderr (stdout занят JSON-RPC), с маскированием секретов.
## Логи и наблюдаемость
Логи модуля пишутся в stderr; уровень задаётся `--log-level` или `OMNI_LOG_LEVEL`
(`debug|info|warn|error`). HTTP-сервер `config_server` — источник данных о
задачах и статусах.
+153
View File
@@ -0,0 +1,153 @@
# Конфигурация
Конфиг — JSON-файл (по умолчанию `config.json`), который задаёт подключения к
`config_server` и политику безопасности. Рядом можно положить
`config.local.json` с секретами — он накладывается поверх и не коммитится.
## Как задать путь к конфигу
Приоритет (первый найденный):
1. аргумент инструмента `config_path` (для обёрток; обычно не нужен);
2. флаг запуска `-config /path/config.json`;
3. каталог из переменной окружения `OMNI_CONFIG_DIR` (в нём берётся
`omnichannel-mcp.json`).
Обычный режим — флаг `-config`.
## Структура
```json
{
"servers": [ { … }, { … } ],
"default": "prod",
"read_only": true,
"allow_hosts": ["10.20.30.40"]
}
```
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
| `servers` | массив | — | Подключения к стендам. Обязательно, если не задан shorthand. |
| `default` | строка | первый сервер | Алиас сервера, если в вызове не указан `server`. |
| `read_only` | bool | `true` | Запрещает изменяющие инструменты (безопасный режим). |
| `allow_hosts` | массив | пусто | Если задан — `base_url` любого сервера обязан быть на этих хостах. |
### Поля сервера
```json
{
"alias": "prod",
"base_url": "http://10.20.30.40:5005",
"username": "${OMNI_USER}",
"password": "${OMNI_PASSWORD}",
"readonly_username": "${OMNI_RO_USER}",
"readonly_password": "${OMNI_RO_PASSWORD}",
"gitlab_token": "${OMNI_GITLAB_TOKEN}",
"insecure_skip_verify": false,
"front_roots": ["/srv/omni-front-builds"],
"timeout_sec": 30,
"upload_timeout_sec": 300,
"task_poll_max_sec": 120,
"task_poll_interval_sec": 2,
"max_output_bytes": 100000,
"max_upload_bytes": 536870912,
"max_concurrent_mutations": 1
}
```
| Поле | По умолчанию | Назначение |
|---|---|---|
| `alias` | — | Имя стенда для выбора сервером (`server:"prod"`). |
| `base_url` | — | Адрес API `config_server` (http/https). Обязательно. |
| `username` / `password` | — | Учётная запись с правом записи. |
| `readonly_username` / `readonly_password` | — | Необязательная учётная запись только для чтения (read-инструменты используют её). |
| `gitlab_token` | — | Токен для `download_release` (если не задан — используется значение сервера/окружения). |
| `insecure_skip_verify` | `false` | Отключить проверку TLS-сертификата (самоподписанные стенды). |
| `front_roots` | `[]` | Разрешённые каталоги локальных сборок фронта (jail). Пусто — `update_front` запрещён. |
| `timeout_sec` | `30` | Таймаут обычных запросов. |
| `upload_timeout_sec` | `300` | Таймаут загрузки фронта и синхронной выгрузки релиза. |
| `task_poll_max_sec` | `120` | Максимум ожидания задачи при `wait=true`. |
| `task_poll_interval_sec` | `2` | Интервал опроса задачи. |
| `max_output_bytes` | `100000` | Лимит длины вывода задачи (обрезается, хвост сохраняется). |
| `max_upload_bytes` | `536870912` | Лимит суммарного размера сборки фронта (512 МиБ). |
| `max_concurrent_mutations` | `1` | Максимум одновременных изменяющих операций на сервер. |
## Секреты
Два способа, оба безопасны (не попадают в репозиторий):
**1. Переменные окружения** — подстановка `${VAR}` в `config.json`:
```json
{ "password": "${OMNI_PASSWORD}" }
```
Переменная должна быть задана в окружении процесса MCP-сервера. Если её нет —
сервер откажется стартовать и явно укажет имя переменной (fail-closed).
**2. `config.local.json`** — файл рядом с `config.json` (в `.gitignore`),
накладывается поверх: объекты сливаются, серверы — по `alias`.
```json
{
"servers": [
{ "alias": "prod", "password": "реальный-пароль" }
],
"read_only": false
}
```
## Shorthand для одного сервера
Вместо массива `servers` можно задать корневые поля — это один сервер с
алиасом `default`:
```json
{
"base_url": "http://10.20.30.40:5005",
"username": "${OMNI_USER}",
"password": "${OMNI_PASSWORD}"
}
```
## Несколько стендов
Каждому стенду — свой `alias`; инструменты принимают аргумент `server`:
```json
{
"servers": [
{ "alias": "prod", "base_url": "http://10.20.30.40:5005", "username": "${P_USER}", "password": "${P_PASS}" },
{ "alias": "stage", "base_url": "http://10.20.31.40:5005", "username": "${S_USER}", "password": "${S_PASS}" }
],
"default": "prod",
"allow_hosts": ["10.20.30.40", "10.20.31.40"]
}
```
Пример целиком — [../examples/config.multi-server.json](../examples/config.multi-server.json).
## Режим «только чтение» (наблюдатель)
Для мониторинга удобно поднять отдельный инстанс без права изменений:
```json
{
"servers": [{ "alias": "prod", "base_url": "http://10.20.30.40:5005",
"readonly_username": "${OMNI_RO_USER}",
"readonly_password": "${OMNI_RO_PASSWORD}" }],
"read_only": true
}
```
Пример — [../examples/config.readonly-observer.json](../examples/config.readonly-observer.json).
## Проверка конфига
```bash
./omnichannel-mcp --check-config -config config.json
```
Печатает итоговый (слитый) конфиг с замаскированными секретами, список серверов
и `default`. Код возврата ≠ 0 при ошибке — удобно для CI.
+132
View File
@@ -0,0 +1,132 @@
# Развёртывание
Два пути: **быстрый** (для уже зарегистрированного сервиса — изменить compose/env
и поднять) и **полный релиз** (схема + манифест → выгрузка релиза → запуск на
одном или нескольких серверах).
> Перед изменяющими операциями убедитесь, что `read_only=false` в конфиге, а
> ассистент запрашивает подтверждение (`confirm`) на разрушительные шаги.
## Быстрый путь: один уже известный сервис
```text
Промпт: «Обнови compose сервиса demo_web на образ nginx:1.27, подними его и дождись результата.»
```
Вызовы:
1. `list_applications` — найти `demo_web` и его `app_id` (или `get_application`, если id известен).
2. `set_compose {app_id, content}` — записать новый compose (создаётся версия, конфиг уходит агенту).
При необходимости — `set_env {app_id, filename, content}`.
3. `deploy {app_id, wait:true}` — `docker-compose up -d` и ожидание задачи.
4. `overview` — убедиться, что сервис поднялся.
Для перезапуска — `restart`; для остановки — `down {confirm:true}`.
## Полный релиз: один сервер
Минимальная схема — один хост:
```json
{
"vm1": { "ip": "10.20.30.11", "services": ["user_system_front"] }
}
```
```text
Промпт: «Сохрани schema.json и manifest.json, разложи хост, скачай релиз с образами,
дождись завершения и запусти сервисы.»
```
1. `save_deployment_files {schema, manifest}`
2. `ip_match` — проверить, что IP хоста сопоставлен агенту.
3. `seed_hosts` — создать запись хоста до выхода агента.
4. `download_release {load_images:true, confirm:true}` → `{"status":"started"}`
5. `release_job` (опрос) — `status: running` → `completed`
6. `start_services` → `start_task_ids`
7. `deployment_tasks` / `get_task {task_id, wait:true}` — результат запуска
8. `overview` — сводка
## Полный релиз: несколько серверов
Схема описывает все VM и их сервисы:
```json
{
"vm-node-1": { "ip": "10.20.30.11", "services": ["chats", "user_system"] },
"vm-node-2": { "ip": "10.20.30.12", "services": ["report_system"] }
}
```
Манифест сопоставляет сервис и пакет релиза (URL архива в GitLab):
```json
{
"services": {
"chats": { "package": "https://gitlab.example/api/v4/.../chats.tar.gz" },
"user_system": { "package": "https://gitlab.example/api/v4/.../user_system.tar.gz" },
"report_system": { "package": "https://gitlab.example/api/v4/.../report_system.tar.gz" }
}
}
```
Порядок:
1. `save_deployment_files {schema, manifest}` (при необходимости — `prebuild_vars`).
2. `ip_match` — увидеть `matched_pairs` и `unmatched_schema_ips`.
Если IP в схеме не совпадают с IP агентов — задать соответствие через
`save_deployment_files {ip_overrides}`:
```json
{ "10.20.30.11": "10.20.30.101", "10.20.30.12": "10.20.30.102" }
```
затем снова `ip_match`.
3. `fill_vars` (опционально) — заполнить `prebuild_vars` из схемы.
4. `seed_hosts` — создать записи хостов.
5. `download_release {load_images:true, confirm:true}` — запускает фоновую
выгрузку пакетов и образов; прогресс — `release_job`
(`phase`, `images_pulled`/`images_total`).
Без образов: `download_release {load_images:false}` вернёт
`downloaded_services` и `sync_task_ids`.
6. `start_services` → `start_task_ids` (по одной задаче на хост).
7. `deployment_tasks` и `get_task {wait:true}` — дождаться результата по каждому хосту.
8. `overview` — итоговая сводка; `list_applications` — статусы сервисов.
> Токен GitLab: задайте `gitlab_token` в конфиге сервера (или переменной
> окружения) — тогда не придётся передавать секрет в аргументе.
## Обновление фронта
Фронт-сервисы (`user_system_front`, `chat_widget`, `scenario_front` и др.)
принимают собранную папку.
1. В конфиге сервера задайте `front_roots` — каталог, куда вы кладёте сборки:
```json
"front_roots": ["/srv/omni-front-builds"]
```
2. ```text
Промпт: «Обнови фронт user_system_front из /srv/omni-front-builds/us.»
```
`update_front {app_id, build_dir:"/srv/omni-front-builds/us", confirm:true}` →
задача агенту → `get_task {wait:true}`.
Каталог обязан лежать внутри `front_roots` (защита от загрузки произвольных
путей); содержимое `build_folder` на хосте заменяется целиком.
## Откат конфигурации
Каждое изменение compose/env создаёт версию, поэтому откат безопасен.
- Compose: `get_application {app_id}` → выбрать `version_id` → `restore_compose {version_id, confirm:true}`.
- Env: `env_versions {app_id, filename}` → `restore_env_version {version_id, confirm:true}`.
Восстановление создаёт **новую** версию (история сохраняется) и отправляет
конфиг агенту.
## Миграции
```text
Промпт: «Запусти миграции сервиса 5 и проверь результат.»
```
`migrate {app_id:5, wait:true}`; журнал последних миграций — в
`get_application` (`migration_logs`).
+109
View File
@@ -0,0 +1,109 @@
# Быстрый старт
`omnichannel-mcp` — это **MCP-сервер**: программа, которую AI-ассистент
(Claude Desktop, Cursor, Continue и любой другой MCP-клиент) запускает как
локальный процесс и через которую получает доступ к платформе Omnichannel.
Сервер не хранит состояние и не имеет UI: он лишь транслирует запросы агента в
HTTP API `config_server`.
## Что понадобится
1. **Запущенный `config_server`** (версия 1.1.0 или новее), доступный по сети.
По умолчанию — `http://<host>:5005`. Проверить: `curl http://<host>:5005/health` → `OK`.
2. **Учётная запись** `config_server` (логин/пароль администратора).
3. **MCP-клиент**, поддерживающий stdio-серверы (Claude Desktop, Cursor и др.).
4. Для сборки — **Go 1.27+** и доступ к общему тулкиту (Go-модуль
`forge-toolkit` на `git.totmin.ru`; задайте `GOPRIVATE=git.totmin.ru`).
## 1. Сборка
```bash
cd omnichannel-mcp
export GOPRIVATE=git.totmin.ru
go build -o omnichannel-mcp .
./omnichannel-mcp --health # -> ok
```
## 2. Конфигурация
Скопируйте пример и задайте адрес и креды:
```bash
cp examples/config.single-server.json config.json
```
```json
{
"servers": [
{
"alias": "prod",
"base_url": "http://10.20.30.40:5005",
"username": "${OMNI_USER}",
"password": "${OMNI_PASSWORD}"
}
],
"default": "prod",
"read_only": false
}
```
Секреты удобно держать в переменных окружения или в `config.local.json`
(подробно — [configuration.md](configuration.md)).
Проверьте конфиг (секреты маскируются) — это самая частая причина проблем:
```bash
export OMNI_USER=admin OMNI_PASSWORD='...'
./omnichannel-mcp --check-config -config config.json
```
## 3. Подключение к MCP-клиенту
Общий вид конфигурации (для любого клиента, поддерживающего `mcpServers`):
```json
{
"mcpServers": {
"omnichannel-mcp": {
"command": "/opt/omnichannel-mcp/omnichannel-mcp",
"args": ["-config", "/opt/omnichannel-mcp/config.json"],
"env": {
"OMNI_USER": "admin",
"OMNI_PASSWORD": "СЕКРЕТ"
}
}
}
}
```
Готовые сниппеты для конкретных клиентов — [mcp-clients.md](mcp-clients.md).
## 4. Проверка
Попросите ассистента:
> «Проверь подключение к Omnichannel и покажи, что сейчас на стенде.»
Ассистент вызовет `server_info` (версия API и режим), затем `overview` (сводка).
Если видите корректную версию `1.1.0` и список сервисов — всё работает.
## 5. Первый деплой
Самый быстрый сценарий для уже зарегистрированного сервиса:
> «Обнови compose сервиса `demo_web` на образ `nginx:1.27`, подними его и
> дождись результата.»
Последовательность вызовов и подробные сценарии (1 сервер, N серверов, фронт,
откат) — [deployment.md](deployment.md). Готовые промпты —
[../examples/prompts.md](../examples/prompts.md).
## Демо-стенд (без прода)
Поднять локальный `config_server` и прогнать сквозной пример:
```bash
docker compose -f examples/demo/docker-compose.yml up -d
go build -o omnichannel-mcp .
python3 examples/demo/demo.py
```
+67
View File
@@ -0,0 +1,67 @@
# Подключение к MCP-клиентам
`omnichannel-mcp` — обычный stdio-сервер. Достаточно указать команду запуска,
аргумент `-config` и (при необходимости) переменные окружения с секретами.
## Claude Desktop
Файл конфигурации:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"omnichannel-mcp": {
"command": "/opt/omnichannel-mcp/omnichannel-mcp",
"args": ["-config", "/opt/omnichannel-mcp/config.json"],
"env": {
"OMNI_USER": "admin",
"OMNI_PASSWORD": "СЕКРЕТ"
}
}
}
}
```
## Cursor
Файл `~/.cursor/mcp.json` (или `.cursor/mcp.json` в проекте):
```json
{
"mcpServers": {
"omnichannel-mcp": {
"command": "/opt/omnichannel-mcp/omnichannel-mcp",
"args": ["-config", "/opt/omnichannel-mcp/config.json"],
"env": { "OMNI_USER": "admin", "OMNI_PASSWORD": "СЕКРЕТ" }
}
}
}
```
## Любой другой stdio-клиент
Форма одна: `command` + `args` + `env`. Минимально достаточно:
```json
{ "command": "/opt/omnichannel-mcp/omnichannel-mcp",
"args": ["-config", "/opt/omnichannel-mcp/config.json"] }
```
Если секреты лежат в `config.local.json`, переменные окружения не нужны.
## Проверка подключения
- Из терминала: `./omnichannel-mcp --check-config -config config.json` и
`./omnichannel-mcp --health`.
- В клиенте: попросите ассистента вызвать `server_info` — он покажет версию API
и режим. Затем `overview` — сводку по стенду.
## Несколько стендов
Один инстанс сервера может обслуживать несколько стендов (`servers` в конфиге).
Тогда в вызовах используется аргумент `server`. Если нужна полная изоляция
(разные пользователи/права) — запустите отдельные инстансы (разные `-config` и,
при желании, `read_only`), каждый со своим именем в `mcpServers`.
+82
View File
@@ -0,0 +1,82 @@
# Эксплуатация
`omnichannel-mcp` работает в **плоскости управления**: он видит статусы,
конфигурацию, задачи и историю, но не заглядывает внутрь контейнеров. Это
разделение осознанное — так сервер остаётся безопасным и предсказуемым.
## Что можно, а что нет
| Задача | Инструменты этого сервера | Вне него |
|---|---|---|
| Статусы сервисов и хостов | `overview`, `list_applications`, `service_map` | — |
| Аудит и разбор задач | `list_tasks`, `get_task`, `deployment_tasks` | — |
| Конфигурация compose/env, версии, откат | `get_application`, `get_config`, `env_files`, `compose_version`, `env_versions`, `restore_compose`, `restore_env_version` | — |
| Жизненный цикл сервиса | `deploy`, `restart`, `down`, `migrate` | — |
| Развёртывание релиза | `save_deployment_files`, `seed_hosts`, `fill_vars`, `download_release`, `release_job`, `start_services` | — |
| **Логи контейнеров** | — | отдельный MCP-сервер SSH/`docker logs` |
| **Ресурсы (CPU/RAM/диск)** | — | мониторинг (SSH, Zabbix, Prometheus) |
| **Данные в БД** | — | отдельный MCP-сервер для БД |
| **HTTP-проверки сервисов** | — | отдельный MCP-сервер для HTTP/веб |
Иными словами: этим сервером удобно отвечать на «что и с какой конфигурацией
развёрнуто и что сломалось при деплое», а диагностику рантайма подключайте
дополнительными MCP-серверами.
## Типовые сценарии
### «Что сейчас на стенде?»
> «Покажи сводку по стенду prod.»
`overview` вернёт health, статистику, сервисы не в статусе OK и последние
упавшие задачи — это самый быстрый «снимок».
### «Почему упал деплой?»
1. `list_tasks {status:"failed", limit:20}` — найти задачу.
2. `get_task {task_id}` — прочитать `output` (stdout/stderr). По умолчанию
оставляется **хвост** вывода, где обычно и причина; при необходимости
увеличить `output_bytes`.
3. При необходимости сравнить конфиг: `get_application {app_id}`.
### «Сервис не поднялся / зависает»
1. `get_application {app_id}` — статус, текущий compose/env, журнал миграций.
2. `list_tasks {host_ip, task_type}` — история операций по хосту.
3. Повторить контролируемо: `restart {app_id, wait:true}` и снова `get_task`.
4. Если внутри сервиса — переключиться на SSH-инструменты (логи).
### «Нужно поправить конфигурацию»
1. `get_config {app_id}` / `get_application {app_id}` — текущее.
2. `set_env` / `set_compose` — изменить (создаётся версия).
3. `deploy` или `restart` — применить на хосте.
4. Проверить `get_task`/`overview`. При неудаче — откат (`restore_*`).
### «Проверить релиз»
- `release_job` — статус фоновой выгрузки (фаза, прогресс образов).
- `deployment_tasks` — последние sync/start-задачи.
- `ip_match` — корректно ли сопоставлены хосты и агенты.
### «Обслуживание»
- `stats` — сколько версий и логов накопилось.
- `cleanup_old_versions {days:180, confirm:true}` — удалить устаревшие версии
(текущие не трогаются).
## Полезные фильтры
- `list_tasks` ограничивает выборку (`limit` ≤ 500) и фильтрует по `host_ip`,
`status`, `task_type`.
- `list_applications {include_presence:true}` включает служебные записи хостов
(хост без развёрнутых сервисов).
## Ограничения и защита
- Длинные выводы обрезаются (`max_output_bytes`), чтобы не переполнить контекст
ассистента.
- Ожидание задач (`wait`) ограничено `task_poll_max_sec`; если задача не успела —
вернётся её текущее состояние, а не ошибка.
- Мутации сериализуются на сервер (`max_concurrent_mutations`) — параллельные
`restart` не наложатся друг на друга.
+91
View File
@@ -0,0 +1,91 @@
# Безопасность
`omnichannel-mcp` даёт ассистенту право управлять платформой, поэтому
безопасность строится в несколько независимых слоёв: конфигурация модуля,
подтверждения, и ограничения на стороне `config_server`.
## Модель угроз
- **Ошибочный или злонамеренный вызов управляющего инструмента** (остановить
прод, залить чужой конфиг).
- **Утечка секретов** (пароли, токен GitLab) в логи, в контекст модели, в
репозиторий.
- **Доступ не туда** (SSRF, подмена хоста стенда).
- **Чрезмерные привилегии** (учётная запись только для чтения там, где нужен
мониторинг).
## Слои защиты
### 1. Учётные данные и секреты
- Пароли и токены — только из `${VAR}` окружения или `config.local.json`
(в `.gitignore`). В репозитории — лишь шаблон.
- `--check-config` печатает конфиг с маской секретов — можно безопасно проверять
настройку и выкладывать вывод в тикеты.
- Секреты не логируются: логи идут в stderr с маскированием, токен GitLab в
выводе не отражается.
### 2. Режим «только чтение»
- `read_only: true` (значение по умолчанию) запрещает все изменяющие инструменты.
- Для наблюдательных агентов поднимайте **отдельный инстанс** с `read_only: true`
и отдельной read-only учётной записью:
```json
{ "servers": [{ "alias": "prod", "base_url": "http://…",
"readonly_username": "${OMNI_RO_USER}",
"readonly_password": "${OMNI_RO_PASSWORD}" }],
"read_only": true }
```
- Если заданы `readonly_*`-креды, read-инструменты ходят именно под ними —
это least-privilege на стороне модуля.
### 3. Подтверждения (`confirm`)
Разрушительные операции требуют `confirm="true"`:
- `down`, `restore_compose`, `restore_env_version`, `update_front`,
`download_release`, `cleanup_old_versions`.
Это второй слой: даже при включённом `read_only=false` ассистент обязан явно
подтвердить опасное действие. Дополнительно настройте политику подтверждений
вашего MCP-клиента (человеческое «да/нет» на управляющие инструменты).
### 4. Изоляция сети (`allow_hosts`)
Если задан `allow_hosts`, `base_url` любого сервера обязан быть на этих хостах —
защита от опечаток и подмены адреса.
### 5. Файловый jail (`front_roots`)
`update_front` принимает только каталоги внутри `front_roots`; при проверке
разрешаются симлинки и отсекаются побеги (`..`, ссылки наружу). Пусто —
загрузка фронта запрещена полностью.
### 6. Ограничение нагрузки
- Таймауты (`timeout_sec`, `upload_timeout_sec`), лимиты вывода
(`max_output_bytes`) и размера загрузки (`max_upload_bytes`).
- Семафор мутаций (`max_concurrent_mutations`) предотвращает наложение операций.
- GET-запросы повторяются при сетевом сбое; **мутации не повторяются** — действие
не выполнится дважды.
## Что модуль НЕ делает
- Не работает с агентскими эндпоинтами `config_server`
(`/api/register`, `/api/tasks/<host>`) — только с пользовательским API.
- Не хранит состояние и не пишет секреты на диск.
- Не выполняет код на хостах: все действия выполняет агент `config_server`.
## Рекомендации оператору
1. Заведите **отдельные учётные записи**: read-only для мониторинга,
полную — только для деплой-агента.
2. Для мониторинга запускайте инстанс с `read_only: true`.
3. Держите секреты в окружении/`config.local.json`, не в `config.json`.
4. Включите подтверждения управляющих инструментов в MCP-клиенте.
5. Ограничьте сетевой доступ к `config_server` (VPN/private network); наружу
порт 5005 не публикуйте.
6. Об уязвимостях сообщайте в службу безопасности компании (не публикуйте в
общих трекерах).
+80
View File
@@ -0,0 +1,80 @@
# Справочник инструментов
36 инструментов, сгруппированы по фазам. Общие аргументы:
- `server` — алиас стенда из конфига (по умолчанию `default`). Необязателен.
- `app_id` — идентификатор сервиса (см. `list_applications`).
- `confirm` — `true` для разрушительных операций.
Обозначения: **R** — только чтение; **W** — изменяет; **W!** — изменяет и
требует `confirm="true"`.
## Наблюдение (observe)
| Инструмент | Тип | Аргументы | Что делает |
|---|---|---|---|
| `server_health` | R | — | Живость `config_server` (`GET /health`). |
| `whoami` | R | — | Кто авторизован. |
| `server_info` | R | — | Версия API (capability-probe), адрес, режим `read_only`. Вызывать первым. |
| `list_applications` | R | `include_presence` | Список сервисов с хостами и статусами. |
| `get_application` | R | `app_id` | Детали: compose, версии, env-файлы, журнал миграций. |
| `get_config` | R | `app_id` | Текущие compose и env сервиса. |
| `env_files` | R | `app_id` | Имена текущих env-файлов. |
| `compose_version` | R | `version_id` | Содержимое версии compose. |
| `env_version` | R | `version_id` | Содержимое версии env-файла. |
| `env_versions` | R | `app_id`, `filename` | Все версии конкретного env-файла. |
| `list_tasks` | R | `host_ip`, `status`, `task_type`, `limit` | Задачи агентов (аудит, поиск failed). |
| `get_task` | R | `task_id`, `wait`, `tail`, `output_bytes` | Статус/вывод задачи; `wait=true` ждёт завершения. |
| `get_deployment_files` | R | — | schema, manifest, prebuild_vars, ip_overrides. |
| `ip_match` | R | — | Сопоставление IP схемы и агентов. |
| `release_job` | R | — | Статус фоновой выгрузки релиза. |
| `deployment_tasks` | R | — | Последние release-задачи (sync/start). |
| `stats` | R | — | Счётчики версий compose/env и логов миграций. |
| `service_map` | R | — | Карта хостов и сервисов. |
| `overview` | R | — | Сводка: health, stats, сервисы не в OK, failed-задачи. |
| `release_status` | R | — | Сводка развёртывания: job, задачи, приложения, IP. |
## Конфигурация (configure)
| Инструмент | Тип | Аргументы | Что делает |
|---|---|---|---|
| `set_compose` | W | `app_id`, `content` | Задать compose, создать версию, отправить агенту. |
| `set_env` | W | `app_id`, `filename`, `content` | Задать env-файл, создать версию, отправить агенту. |
| `restore_compose` | W! | `version_id`, `confirm` | Восстановить версию compose. |
| `restore_env_version` | W! | `version_id`, `confirm` | Восстановить версию env-файла. |
| `update_config` | W | `app_id` | Повторно отправить текущий конфиг агенту. |
## Жизненный цикл (operate)
| Инструмент | Тип | Аргументы | Что делает |
|---|---|---|---|
| `deploy` | W | `app_id`, `wait` | `docker-compose up -d`. |
| `restart` | W | `app_id`, `wait` | `down` + `up -d`. |
| `down` | W! | `app_id`, `confirm`, `wait` | `docker-compose down`. |
| `migrate` | W | `app_id`, `wait` | `docker-compose run migration`. |
| `update_front` | W! | `app_id`, `build_dir`, `confirm` | Загрузить сборку фронта (каталог внутри `front_roots`). |
## Развёртывание релиза (release)
| Инструмент | Тип | Аргументы | Что делает |
|---|---|---|---|
| `save_deployment_files` | W | `schema`, `manifest`, `prebuild_vars`, `ip_overrides` | Сохранить файлы развёртывания (только переданные поля). |
| `seed_hosts` | W | — | Создать хосты из schema.json. |
| `fill_vars` | W | — | Заполнить prebuild_vars из схемы. |
| `download_release` | W! | `load_images`, `gitlab_token`, `confirm` | Выгрузить релиз; `load_images=true` — фон. |
| `start_services` | W | — | Создать задачи запуска сервисов по схеме. |
## Обслуживание (maintain)
| Инструмент | Тип | Аргументы | Что делает |
|---|---|---|---|
| `cleanup_old_versions` | W! | `days`, `confirm` | Удалить версии/логи старше N дней. |
## Результаты и ошибки
- Успех — читаемый JSON.
- **Доменная ошибка** (неверный `app_id`, нет confirm, отказ сервера) возвращается
как результат инструмента с текстом — ассистент видит её и может исправить.
- **Инфраструктурная ошибка** (нет сети) — как ошибка протокола.
- Длинный вывод задачи обрезается (по умолчанию с сохранением хвоста — там
обычно причина сбоя); `get_task` принимает `output_bytes` и `tail`.