Перейти к содержимому

Скилы через MCP

Скилл — это набор инструментов, который воркер получает от MCP-сервера (Model Context Protocol). Скиллы не пишутся под каждую задачу: берётся готовый сервер — Postgres, web-fetch, файловая система — и запускается там, где доступна его цель.

Особенность Nythronix в том, что воркер изолирован по сети (ему доступны только control plane и LLM), а базы, логи и сервисы живут на удалённых машинах за агентами. Поэтому MCP-сервер запускается на машине как подпроцесс агента, а воркер обращается к нему через control plane:

воркер (MCP-клиент)
│ HTTP JSON-RPC, один вызов = один запрос/ответ
▼
control plane /api/agents/:id/mcp/:server/… (релей, токен воркера, аудит)
│ по WebSocket-соединению агента
▼
агент на машине → MCP-сервер на loopback → цель (локальная БД, файлы, сервис)

Ничего открывать наружу не нужно: агент и так держит исходящее соединение с control plane, а MCP-сервер ходит к цели по сети самой машины.

Собственные инструменты воркера — память (remember / read_history) и запуск shell с подтверждением (run_shell) — остаются внутри воркера и через MCP не проходят. Подробнее о них в разделе Воркеры и каналы.

Схема защиты — каталог на машине ограничивает control plane. Control plane никогда не отправляет агенту команду запуска; он передаёт только имя скилла и параметры. Соответствие имени и команды хранится у владельца машины в файле /etc/nythronix/mcp-catalog.json:

[
{
"name": "postgres",
"port": 9912,
"params": ["db_uri"],
"read_only": true,
"command": ["mcp-proxy", "--stateless", "--host", "127.0.0.1", "--port", "$PORT", "--",
"postgres-mcp", "--access-mode", "restricted", "$db_uri"]
}
]

В command подстановка $PORT заменяется портом записи, $<param> — значением объявленного параметра, которое пришло с control plane. Скомпрометированный или чужой control plane может включить лишь то, что владелец уже внёс в каталог — тот же принцип, что и с локальным ограничением sudo.

Дальше цепочка такая:

  1. Агент при подключении объявляет control plane свой каталог (имена, параметры, признак read_only; сами команды не передаются) и список уже запущенных серверов.
  2. Администратор в разделе Админ → Агенты открывает у машины панель Скилы, включает нужные записи каталога, заполняет параметры и нажимает Сохранить. Control plane отправляет агенту желаемый набор; агент запускает, останавливает или перезапускает подпроцессы, чтобы привести состояние в соответствие, и сохраняет применённый набор локально — после перезагрузки машины скиллы поднимаются сами. Если агент офлайн, набор можно править, он применится при переподключении. Изменение фиксируется в аудите как admin.agent.mcp_config_set.
  3. Воркер при старте обходит машины организации, забирает у каждой список серверов и загружает их инструменты под именами <машина>__<сервер>__<инструмент>, например nyx-db__postgres__execute_sql.

Для машин без каталога поддерживается прежний формат /etc/nythronix/mcp-servers.json с готовыми командами; его записи стартуют при загрузке агента.

Поле скилы у воркера (в интерфейсе — «скилы, напр. postgres (пусто = все)») ограничивает, какие серверы он загружает. Это список имён скилл-серверов через пробел или запятую, допускаются glob-шаблоны, регистр не важен. Пустое значение, all или * означают «все скиллы». Воркер с пресетом postgres получит только инструменты Postgres и пропустит остальные серверы: меньше инструментов — точнее работает модель и меньше поверхность привилегий. Пресет можно указать и при создании канала.

Подтверждение и аудит изменяющих инструментов

Заголовок раздела «Подтверждение и аудит изменяющих инструментов»

Инструмент считается безопасным и выполняется без вопросов, если сервер в каталоге помечен read_only: true или сам инструмент объявляет readOnlyHint. Всё остальное — в том числе инструменты без явной аннотации — перед вызовом попадает в канал карточкой «нужно подтверждение» с блоком «Скил, который выполнится» и аргументами. Отклонённый вызов до машины не доходит: воркер получает ответ «не выполнено» и меняет план.

Готовые серверы часто не проставляют readOnlyHint на каждый инструмент, поэтому владелец машины один раз объявляет read_only на весь сервер (например, для Postgres в режиме restricted), и лишние запросы подтверждения исчезают.

Каждый выполненный вызов инструмента control plane записывает в аудит как agent.mcp.call с указанием организации, инициатора, машины, сервера и инструмента — включая автоматически выполненные вызовы read-only серверов. Отклонённый вызов виден только как chat.approval.decided.

  • Отдельный процесс. Каждый MCP-сервер — дочерний подпроцесс агента на loopback. При отключении скилла или падении агента подпроцесс завершается вместе с ним.
  • Без root. Перед запуском агент понижает привилегии подпроцесса до пользователя nyx-worker — того же, под которым выполняются команды воркера. Если агент сам запущен не от root, понижение не выполняется.
  • Ограниченный выход в сеть. Скрипт deploy/host/skills-egress.sh (и его systemd-юнит nyx-skills-egress.service) перенаправляет исходящие соединения пользователя nyx-worker на порт 443 в локальный SNI-фильтр: разрешённые домены проходят, остальные обрываются. TLS при этом остаётся сквозным. Соединение самого агента с control plane и локальные цели (Postgres на 5432, loopback) не затрагиваются.
Окно терминала
ALLOW="example.com api.github.com" ./skills-egress.sh up
./skills-egress.sh status

Для внешних сервисов есть готовый образ deploy/docker/Dockerfile.skills-host: контейнер с агентом, mcp-proxy, mcp-server-fetch и встроенным фильтром выхода, который регистрируется в control plane как отдельная машина skills-host. Ему нужна capability CAP_NET_ADMIN, а разрешённые домены задаются переменной SKILLS_ALLOW.

Postgres. На машине с базой установите mcp-proxy и postgres-mcp, добавьте запись postgres из примера выше в каталог, перезапустите агент. В админке включите скилл, укажите db_uri с учётной записью только для чтения и сохраните. Воркер с пресетом postgres получит около девяти инструментов (execute_sql, list_schemas, explain_query, analyze_db_health и другие) и будет выполнять SQL-запросы без подтверждений, при этом каждый вызов останется в аудите.

Web. На хосте скиллов запись fetch (mcp-proxy --stateless … -- mcp-server-fetch) даёт инструмент fetch для загрузки страниц. Держите её с read_only: false и ограничьте домены через skills-egress.sh: запрос к разрешённому домену вернёт содержимое, к любому другому — ошибку, даже после подтверждения оператором.

Большинство MCP-серверов говорят только по stdio; адаптер mcp-proxy --stateless превращает их в HTTP-сервис, который умеет проксировать control plane.