Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты новостей
Схема архитектуры безопасного MCP-сервера с валидацией запросов и шлюзом авторизации для ИИ-агента

Безопасность и архитектура MCP-серверов: как доверить LLM-агентам управление боевой инфраструктурой

Инженерный опыт проектирования production-серверов по протоколу Model Context Protocol: почему обернуть SDK в MCP — это лишь 10% работы, как разделить инструменты на безопасные и мутирующие, внедрить Human-in-the-loop подтверждения и изолировать контекст модели при работе с критическими серверами.

Безопасность и архитектура MCP-серверов: как доверить LLM-агентам управление боевой инфраструктурой

Интеграция больших языковых моделей (LLM) с реальной инфраструктурой стала одним из ключевых направлений автоматизации. Протокол Model Context Protocol (MCP), предложенный компанией Anthropic, быстро превратился в открытый стандарт для подключения внешних инструментов к ассистентам — от Claude Code и Cursor до автономных инженерных сред. Протокол определяет способ, с помощью которого клиент поднимает сервер инструментов дочерним процессом и обменивается с ним сообщениями по стандарту JSON-RPC через стандартные потоки ввода-вывода (stdio).

Однако создание надежного MCP-сервера для боевой инфраструктуры вскрывает разрыв между прототипом и эксплуатацией. Превращение готовой клиентской библиотеки (SDK) в MCP-сервер решает лишь около 10% задачи. Остальные 90% усилий уходят на проектирование границ доверия: определение того, что модели разрешено выполнять на серверах, как предотвратить случайное удаление данных при галлюцинациях и как защитить контекстное окно от переполнения и утечки учетных записей. Опыт разработки production-сервера marzban-mcp для управления панелью администрирования наглядно демонстрирует архитектурные принципы безопасного делегирования полномочий автономным агентам.

Инструменты проектируются под намерения модели, а не под эндпоинты API

Типичная ошибка при создании MCP-сервера — прямое сопоставление методов API с инструментами модели в соотношении один к одному. Если в библиотеке пятьдесят методов, генерация пятидесяти инструментов приводит к двум системным проблемам:

  • Деградация выбора и перерасход токенов. Полный список инструментов со схемами передается в контекст модели при каждом запросе. Десятки схем увеличивают затраты и снижают точность: модель начинает путать похожие операции и выбирать неверные инструменты.
  • Несоответствие интерфейса когнитивной модели. Традиционные API оптимизированы для программистов. Метод modifyUser может принимать объект со статусом (status: "active"). Языковая модель, получив задачу «разблокировать аккаунт», вынуждена угадывать значение в перечислении, рискуя ошибиться и получить отказ валидации.

Эффективный подход — проектирование инструментов вокруг конкретных намерений (intent-based design). Вместо одного перегруженного метода создаются узкие атомарные операции: marzban_users_activate, marzban_users_deactivate и marzban_users_hold. Схема их аргументов содержит только имя пользователя.

Аналогично создаются составные инструменты для операций, отсутствующих в базовом API. Например, продление подписки (marzban_users_extend) объединяет в одну транзакцию чтение текущего состояния, расчет новой даты окончания и запись изменений, исключая риск арифметической ошибки модели между шагами.

Дополнительно внедряется обязательное пространство имен (префикс marzban_), предотвращающее коллизии при подключении нескольких серверов. Описания инструментов формулируются с явными ограничениями: в тексте указывается, когда инструмент вызывать не следует (например: «Для поиска одного пользователя используйте marzban_users_get, не перебирайте список постранично»).

Разграничение прав и изоляция профилей доступа

Боевая панель содержит как безопасные операции чтения, так и разрушительные команды: удаление аккаунтов или перезапуск ядра (marzban_core_restart), обрывающий активные соединения. Все операции разделяются на три категории доступа:

Категория доступаТип операцийПримеры инструментовПотенциальный риск
readЧтение состояния и метрикmarzban_users_get, marzban_system_statsМинимальный (риск утечки данных в контекст)
writeБезопасные модификацииmarzban_users_activate, marzban_users_extendСредний (обратимые изменения бизнес-логики)
destructiveНеобратимые мутацииmarzban_users_delete, marzban_config_updateКритический (потеря данных, остановка сервиса)

Профиль доступа задается через переменные окружения сервера (readonly, standard, full). Фильтрация инструментов выполняется на этапе регистрации при запуске процесса, а не в момент вызова. В режиме readonly деструктивные инструменты физически отсутствуют в ответе на запрос списка утилит (tools/list), поэтому модель не пытается их вызвать.

Аннотации безопасности (readOnlyHint, destructiveHint), передаваемые хосту, выводятся автоматически из категории доступа инструмента и не задаются вручную, исключая человеческий фактор.

Двухэтапное подтверждение разрушительных действий

Стандартное окно клиента показывает лишь имя функции и сырой JSON аргументов, приучая пользователей подтверждать вызовы вслепую. Для надежного контроля внедряется двухэтапный механизм с оценкой последствий:

  1. Генерация описания последствий (describeConsequences). Сервер запрашивает текущее состояние объекта и формирует понятное описание изменений: точный дифф секций конфигурации или объем трафика удаляемого пользователя.
  2. Выпуск криптографического токена подтверждения. Первый вызов деструктивного инструмента возвращает предупреждение и одноразовый токен. Токен содержит идентификатор UUID jti, имя инструмента и хеш SHA-256 от канонизированных аргументов. Ключ подписи генерируется в памяти процесса и действует 5 минут.
  3. Исполнение по токену. Мутация выполняется только тогда, когда модель повторяет вызов с полученным токеном. Токен на удаление одного пользователя невозможно применить к другому. Для предварительного просмотра без блокировок поддерживается флаг симуляции (dryRun: true).
export const userDeleteTool = defineTool({
  name: 'marzban_users_delete',
  title: 'Удаление пользователя',
  description: 'Удаляет аккаунт. Требует токен подтверждения.',
  inputSchema: userDeleteInputSchema,
  outputSchema: userDeleteOutputSchema,
  scope: 'destructive',
  describeConsequences: async (args, ctx) => {
    const user = await ctx.sdk.user.getUser(args.username);
    return `Удаление пользователя ${user.username} (трафик: ${user.usedTrafficBytes} байт). Действие необратимо.`;
  },
  handler: async (args, ctx) => {
    await ctx.sdk.user.removeUser(args.username);
    return { username: args.username, success: true };
  },
});

Управление контекстом, транспорт и безопасность секретов

Ответы инфраструктурных API часто содержат десятки избыточных полей. Архитектура вывода разделяется на два слоя: предметный слой проекции (View), отбирающий только необходимые для решения поля, и слой рендеринга (Renderer), форматирующий строки в текст, таблицу или JSON. Поле content содержит сжатый текст для модели, а structuredContent сохраняет полные данные для программных клиентов. Обрезка по лимиту символов (maxChars) выполняется по границам строк с маркером [Данные усечены...].

Транспорт stdio требует, чтобы поток stdout использовался исключительно для пакетов JSON-RPC; любые логи приложения принудительно направляются в stderr. Учетные данные считываются только из переменных окружения и никогда не принимаются через аргументы модели, что предотвращает атаки Prompt Injection. Перед отправкой сообщений об ошибках в контекст модели из них автоматически вырезаются токены Bearer, JWT и пароли.

Архитектурный чек-лист для релиза MCP-сервера

Перед развертыванием MCP-сервера в боевом окружении рекомендуется проверить:

  1. Пространство имен и намерения: Инструменты имеют уникальный префикс и отражают задачи пользователя, а не методы API.
  2. Изоляция профилей: Деструктивные команды физически скрыты в режиме readonly при регистрации.
  3. Шлюз подтверждения: Опасные операции требуют одноразового токена с проверкой хеша аргументов и описанием последствий.
  4. Проекция вывода: Ответы сжаты предметным слоем View; размер текста ограничен безопасным лимитом.
  5. Чистота транспорта: Поток stdout выделен только под JSON-RPC, журналирование перенаправлено в stderr.
  6. Изоляция секретов: Учетные данные считываются только из окружения; ошибки маскируют токены.
  7. Тестирование: Логика подтверждений покрыта юнит-тестами, а интеграционные сценарии проверены в изолированном контейнере.

Техническая спецификация стандарта доступна в официальной документации Model Context Protocol Specification. Описанные паттерны позволяют превратить экспериментальные скрипты в безопасный и предсказуемый инструмент управления инфраструктурой.