Initial commit: omnichannel-mcp — MCP-сервер управления платформой Omnichannel
This commit is contained in:
@@ -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`-конфигом.
|
||||
@@ -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` — источник данных о
|
||||
задачах и статусах.
|
||||
@@ -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.
|
||||
@@ -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`).
|
||||
@@ -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
|
||||
```
|
||||
@@ -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`.
|
||||
@@ -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` не наложатся друг на друга.
|
||||
@@ -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. Об уязвимостях сообщайте в службу безопасности компании (не публикуйте в
|
||||
общих трекерах).
|
||||
|
||||
@@ -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`.
|
||||
Reference in New Issue
Block a user