Читать 7 мин

Как лимиты stderr в MCP помогают агентам продолжать работу

Настройте лимиты stderr в MCP, чтобы шумные помощники не расходовали память, не задерживали одобрение, не срывали завершение и не оставляли неполные записи действий.

Как лимиты stderr в MCP помогают агентам продолжать работу

Шумный помощник может остановить запуск агента, не нарушив ни одного сообщения JSON-RPC. Он превращает stderr в очередь без ограничений, а расплачиваться за это приходится другой части стека: канал заполняется, читатель удерживает мегабайты данных, завершение ждёт бесконечно или карточка одобрения появляется уже после того, как пользователь перестал доверять запуску.

Считайте stderr недоверенными входными данными с ограниченным бюджетом. Это может показаться излишней педантичностью, пока не увидишь, как обычная команда выдаёт по одному диагностическому сообщению на каждую повторную попытку, хотя действие, ради которого всё запускалось, уже завершилось, а родительский процесс всё ещё пытается собрать весь этот шум. Решение не в том, чтобы «отключить логирование». Нужно постоянно читать поток, сохранять лишь ограниченный объём, учитывать отброшенные данные и отделить состояние действия от текстового вывода.

Stderr ведёт к обратному давлению

Stderr может заблокировать помощника, если родительский процесс подключил его к каналу и читает этот канал недостаточно быстро. Операционные системы выделяют каналам буферы конечного размера. Когда дочерний процесс заполняет такой буфер, следующая запись ждёт читателя. Если путь успешного завершения у дочернего процесса наступает только после записи диагностики, действие выглядит зависшим, хотя сетевой запрос, SSH-соединение или локальная работа в остальном прошли нормально.

Противоположная проблема тише, но часто обходится дороже. Родительский процесс может своевременно читать stderr и добавлять каждый байт в строку, буфер событий или результат инструмента MCP. Канал не переполняется, зато разговорчивый дочерний процесс способен занять столько памяти, что хост замедлится, столкнётся с нехваткой памяти или начнёт отклонять следующие вызовы. Лимита, который защищает только канал, недостаточно. Лимита, который защищает только память, тоже недостаточно.

Для агентов это особенно важно, потому что они часто запускают несколько задач параллельно. Одна задача может запустить нескольких помощников, а цикл повторных попыток создаст новую волну, пока первая ещё не успела выгрузить вывод. Скорость логирования, безобидная в интерактивном терминале, становится проблемой ресурсов, когда несколько сессий захватывают этот вывод одновременно.

Нужно ограничить три разных объёма:

  • байты, ожидающие чтения в системном канале;
  • байты, которые шлюз хранит в памяти для вызова;
  • байты, которые шлюз передаёт агенту, интерфейсу или проекции журнала.

Не путайте лимит строк с лимитом байтов. В одной строке может оказаться большой фрагмент тела ответа, цепочка сертификатов или сжатый объект ошибки. Не рассчитывайте и на то, что UTF-8 всегда приходит целыми символами. Потоковые читатели получают байты порциями, поэтому лимит должен работать и в случае, когда многобайтный символ пересекает границу порции.

Практический читатель ведёт счётчик байтов для всего потока и отдельный кольцевой буфер для сохраняемого хвоста. После превышения общего лимита он продолжает читать данные, чтобы дочерний процесс мог завершиться. Сохраняемый буфер больше не растёт, а система фиксирует факт усечения. Немедленно завершить дочерний процесс при первом превышении иногда правильно для инструмента, который сам создаёт опасный объём вывода, но для диагностики это плохой вариант по умолчанию. Обычно нужны настоящий код завершения и последние строки, объясняющие причину сбоя.

MCP не смешивает stdout с другими данными

Сервер MCP на stdio должен считать stdout территорией протокола. В рекомендациях спецификации Model Context Protocol для транспорта stdio сказано, что сервер не должен писать в stdout ничего, кроме корректных сообщений MCP. Это легко принять за требование к аккуратному форматированию. На практике оно предотвращает гораздо более неприятную проблему: случайная строка о ходе работы от помощника может сделать JSON недействительным, из-за чего хост разберёт поток неверно и завершит вполне исправную сессию.

Печатайте человеческую диагностику в stderr, но не считайте это безобидным решением. В смысле протокола stderr действительно вне основного канала. Однако сам хост процесса решает, унаследовать этот поток, направить его в канал, захватить, показать в терминале или передать в структурированный журнал. Каждый вариант меняет поведение при сбоях.

Наследование потока подходит локальному разработчику: терминал потребляет вывод, и разработчик его видит. Для шлюза агента это плохой вариант по умолчанию. Произвольный текст попадёт в место, где он может не сохраняться, смешиваться с выводом параллельных вызовов и содержать сведения, которые агент никогда не должен получать. Полный захват полезнее для диагностики, но только при наличии бюджета.

Граница протокола должна оставаться предсказуемой. Сервер MCP должен отправлять корректные сообщения JSON-RPC в stdout, ограничивать собственную диагностику в stderr и запускать помощников с потоками под явным контролем. Помощник должен возвращать структурированные результаты по предназначенному для этого каналу. Ему не следует печатать в stderr объект JSON в надежде, что родительский процесс распознает его позже.

Такое разделение предотвращает распространённую ошибку: stderr начинают использовать как запасной канал ответа. Это не так. У него нет надёжной структуры сообщений, при отмене вывод может оказаться неполным, а в поток могут попасть данные библиотек, которые ничего не знают о вашей модели действий. Если вызывающей стороне нужны число повторных попыток, код удалённой ошибки или список изменённых файлов, добавьте их в структурированный результат. Stderr оставьте для свидетельств, которые могут понадобиться человеку при расследовании сбоя.

Для захвата вывода нужен отдельный бюджет памяти

Чтение stderr в фоновой задаче не делает захват безопасным. Оно лишь переносит узкое место из канала в кучу. Я видел, как хост исправлял взаимную блокировку, читая оба потока параллельно, а потом обнаруживал, что плохой помощник заставляет хранить каждый байт до окончания действия. Процесс завершается, но хост страдает.

Используйте кольцевой буфер для сохраняемой диагностики. После достижения ёмкости он оставляет последние байты, где обычно и находится полезное сообщение об ошибке. Если первая строка важна для вашей среды, сохраняйте также короткое начало, например вызов команды или версию библиотеки. Не храните и начало, и конец без ограничений.

Следующий псевдокод описывает поведение, которое стоит реализовать. Он не зависит от конкретного языка.

on_stderr_chunk(bytes):
  stderr_seen += length(bytes)
  if stderr_seen \u003c= capture_limit:
    append_tail(bytes)
  else:
    append_tail(bytes)       # ring buffer evicts older bytes
    stderr_truncated = true
  continue_reading()

На комментарий стоит обратить внимание. capture_limit должен означать объём сохраняемых данных, а не момент прекращения чтения. В строгой реализации после достижения лимита можно пропускать append_tail и сохранять первые байты. Я предпочитаю хвост, потому что сообщения о сбое часто появляются после страниц вывода о ходе работы. Какой бы вариант вы ни выбрали, укажите его в записи, чтобы позже было понятно, читают ли её начало или конец.

Установите второй лимит для объекта, который передаёт диагностику в контекст агента. Для решения о повторной попытке агенту не нужен журнал размером в несколько мегабайт. Ему достаточно краткого сообщения об ошибке, кода завершения и, в крайнем случае, выбранного хвоста. Если шлюз передаёт необработанный вывод помощника, потому что «он может понадобиться модели», каждый помощник получает возможность вытеснить остальную информацию из контекста задачи.

Не декодируйте и не кодируйте заново большой поток только ради ограничения текста. Считайте исходные байты до создания строк. Декодируйте сохраняемый фрагмент с заменой недопустимых последовательностей и пометьте его как захваченный stderr. Так вы избежите лишнего расхода памяти и ложной уверенности в ситуации, когда помощник по ошибке выдаёт двоичные данные.

Завершённое действие всё ещё может сорваться при остановке

Именно при завершении процесса потоки логов превращаются в вводящие в заблуждение отчёты об инцидентах. Действие могло успешно завершиться на удалённой стороне, помощник мог вывести последнюю диагностику, а родительский процесс всё равно не сформирует результат, потому что ждёт не то событие и не в том порядке.

Типичная последовательность выглядит так:

  1. Шлюз запускает помощника и начинает читать stdout, но во время всплеска stderr чтение отстаёт.
  2. Помощник завершает внешнее действие, а затем записывает достаточно диагностики, чтобы заполнить канал stderr.
  3. Родительский процесс отменяет сессию или достигает крайнего срока и отправляет сигнал завершения.
  4. Родительский процесс ждёт дочерний процесс до закрытия или опустошения читателей потоков.
  5. Один читатель ждёт конца файла, пока другая задача ждёт читателя, и сессия так и не доходит до своей финальной записи.

Внешний эффект уже мог произойти. HTTP-запрос мог быть принят, или SSH-команда могла изменить удалённый файл. Если сообщить об этом вызове просто «время ожидания истекло», оператор получит худший из возможных ответов: он не поймёт, повторит ли новая попытка изменение.

Назначьте каждому помощнику одного владельца, который одновременно управляет четырьмя вещами: дескриптором дочернего процесса, читателем stdout, читателем stderr и отменой. При штатном завершении дождитесь выхода процесса и дочитайте потоки до конца файла, прежде чем формировать итоговый результат. При отмене запросите завершение, продолжайте читать оба потока, подождите ограниченное время для корректного выхода и затем принудительно завершите процесс, если это позволяет платформа. После этого дождитесь завершения читателей и запишите фактически наблюдавшееся состояние выхода.

Не делайте читателей потоков дочерними только обработчику запроса. Обработчик может исчезнуть при отключении клиента. Владелец помощника и его читателей должен жить достаточно долго, чтобы завершить очистку и записать конечное состояние. Иначе процесс агента может завершиться, хост потеряет последние ссылки на читателей, а дочерний процесс останется работать с каналами, которые никто не читает.

С группами процессов нужна такая же осторожность. Оболочка может создать потомков, унаследовавших stderr, а завершение одной оболочки оставит потомка, удерживающего канал открытым. По возможности не используйте оболочку. Если без неё не обойтись, запускайте её в изолированной группе процессов и точно определяйте, каких потомков охватывает отмена. Затем проверьте сценарий, в котором оболочка завершается, а внук продолжает писать.

Время одобрения не должно зависеть от объёма логов

Сначала записывайте жизненный цикл, потом диагностику
Журналы Sessions и Activity отделяют запуск агента от каждого отдельного HTTP- или SSH-вызова.

Время одобрения должно зависеть от состояния действия, а не от скорости, с которой помощник выдаёт диагностику. Если интерфейс показывает одобрение только после того, как помощник сформировал расшифровку предварительной проверки, шумный предварительный этап меняет момент, когда пользователя просят принять решение. Тогда одобрение кажется случайным, и люди начинают подтверждать карточки, не понимая, почему одна из них появилась позже.

Определите переходы до написания кода интерфейса. Вызов может быть получен, проверен, ожидать одобрения, авторизован, отправлен, завершён, отменён или завершиться ошибкой до отправки. Stderr может быть связан с вызовом, но не должен определять переход. Шлюз должен проверить целевой объект и параметры запроса, создать запись вызова и показать требуемое одобрение до запуска действия, для которого оно нужно.

Есть полезное исключение. Иногда помощник нужен, чтобы выяснить, какое действие произойдёт, например преобразовать локальное имя конфигурации в конкретную конечную точку. В таком случае считайте обнаружение отдельной операцией без побочного действия и задайте для её вывода собственный бюджет. Не прячьте действие внутри «предварительной проверки», чтобы потом утверждать, будто пользователь одобрил его позднее.

Задайте для одобрения крайний срок по часам, который не сбрасывается из-за новых сообщений stderr. Пока карточка видна, продолжайте собирать ограниченный хвост, но не перерисовывайте карточку на каждую строку. Предупреждение о повторной попытке или краткое описание ошибки могут быть полезны до одобрения, но должны отображаться как стабильное объяснение, а не как бесконечно движущийся просмотр логов.

В тестах разделяйте согласие и работоспособность. В тесте на поток вывода измеряйте время от корректного запроса вызова до карточки одобрения, генерируя stderr до и после этой точки. Тест медленного читателя должен подтвердить, что карточка остаётся рабочей, пока шлюз читает поток. Тест отмены должен проверить, что закрытие карточки не оставляет работающих помощников и создаёт финальную запись.

Человеческий смысл прост: карточка одобрения должна описывать конкретное действие в конкретный момент. Если вывод логов способен задержать этот момент, изменить его или пережить его, интерфейс сообщает о растерянности процесса, а не даёт пользователю контроль.

В записях вызовов сначала нужны факты жизненного цикла

Запись вызова завершена, когда она объясняет состояние действия, а не когда содержит каждую строку, выданную помощником. Логи это свидетельства. События жизненного цикла это сама запись.

Запишите попытку до отправки. Укажите стабильный идентификатор вызова, идентификатор сессии, тип запрошенного действия, решение об одобрении или отказе и сведения о цели, которые видел пользователь. В момент начала отправки добавьте это событие. При завершении добавьте наблюдавшийся результат: успех, удалённую ошибку, локальную ошибку, отмену, принудительное завершение или неизвестный результат из-за потери границы процесса.

Затем добавьте диагностические метаданные. Как минимум запишите общее число увиденных байтов stderr, число сохранённых байтов, факт усечения, код завершения помощника, если он доступен, и корректность завершения потока. Тогда короткий сохранённый хвост будет честно описан. Позже читатель отличит «команда напечатала это» от «шлюз сохранил последнюю часть того, что напечатала команда».

Форма записи может быть такой небольшой:

{
  "call_id": "c_7f2a",
  "state": "cancelled_after_dispatch",
  "stderr_bytes_seen": 184320,
  "stderr_bytes_retained": 16384,
  "stderr_truncated": true,
  "exit_status": null,
  "stream_end": "reader_completed_after_cancel"
}

Не записывайте exit_status: 0, потому что родительский процесс получил успешное тело ответа. Тело ответа и завершение дочернего процесса это разные наблюдения. Не записывайте state: failed, если отмена произошла после отправки и удалённая сторона могла выполнить действие. Во время реализации это кажется излишней точностью, но в два часа ночи именно она отличает безопасное расследование от слепого повтора.

В документе NIST SP 800-92 Guide to Computer Security Log Management есть важная мысль: управление журналами включает создание, передачу, хранение, анализ и удаление данных, а не только сбор текста. Применяйте эту логику к действиям агента. Если сбор данных может помешать завершению, путь логирования стал частью выполнения. Ему нужны ограничения, состояние и обработка сбоев, как любому другому пути выполнения.

Sallyport строит журналы Sessions и Activity из одного зашифрованного журнала аудита, доступного только для записи и связанного цепочкой хешей. Поэтому вызывающая сторона может отличить запуск агента от отдельных записей действий и не полагаться на расшифровку вывода помощника как на историю.

Ограничивайте шум в трёх местах

Проверяйте чувствительные вызовы отдельно
Назначьте ключу одобрение для каждого вызова, если каждое использование требует отдельного решения человека.

Одного ограничения на верхнем уровне недостаточно для защиты от случайностей. Задайте лимиты на уровне помощника, шлюза и места назначения диагностики. Каждый лимит защищает отдельную границу.

Сначала сделайте помощников менее разговорчивыми по умолчанию. Обычный прогресс выводите только при явно включённом режиме отладки, для последовательности повторных попыток выдавайте одно сводное сообщение и не печатайте тела запросов или ответов без необходимости. Помощник никогда не должен записывать в stderr пароли, заголовки авторизации или материалы закрытых ключей. Маскирование после захвата полезно как дополнительная защита, но оно не отменит утечку секрета, который уже попал в терминал, отчёт о сбое или буфер без ограничений.

Затем заставьте шлюз постоянно читать поток и сохранять ограниченное начало или хвост. Для каждого потока он должен ограничивать максимальную длительность и максимальное число сохраняемых байтов. Нужен также общий диагностический бюджет процесса. Без этого последнего ограничения пятьдесят вызовов, каждый в пределах своего лимита, всё равно могут вместе создать скачок потребления памяти.

Наконец, ограничьте место назначения. Если вы передаёте диагностику вызова в интерфейс, ответ агента или файл, установите там ещё один лимит. Журнал может хранить структурированные поля жизненного цикла и дайджест отброшенного вывода, не сохраняя повторяющийся стек вызовов для каждой повторной попытки.

Используйте явную форму конфигурации. Названия не важны. Важно разделение.

helper_output:
  stderr_retained_per_call_bytes: 16384
  stderr_retained_process_bytes: 262144
  stderr_agent_excerpt_bytes: 4096
  shutdown_grace_seconds: 5
  retain: tail

Эта конфигурация предотвращает распространённую ошибку: команда задаёт лимит ответа агента и считает, что хост защищён. На самом деле хост сначала читает и хранит все данные, а уже потом обрезает ответ. Лимит на один вызов защищает отдельное действие. Лимит процесса защищает параллельные действия. Лимит фрагмента для агента защищает контекст, которым модель должна делиться с остальной частью своей работы.

Не используйте один глобальный переключатель с названием quiet. Из-за него сложнее расследовать сбои в рабочей среде, а разработчики начинают снова включать неограниченные логи, когда им нужны свидетельства. Оставьте обычный вывод кратким, добавьте управляемый режим отладки на короткий период и сохраните в записи вызова факт, что этот режим был включён.

Не следуйте популярному совету перенаправить stderr в /dev/null. Его дают потому, что он сразу устраняет зависание и сохраняет ответы агента аккуратными. Но вместе с этим исчезает первая полезная подсказка, когда SSH-помощник не может пройти аутентификацию, проверка сертификата завершается ошибкой или удалённая команда возвращает неожиданный результат. Вместо этого постоянно читайте поток, ограничивайте его и помечайте назначение данных.

Усечение должно быть заметным, но не драматичным

Усечение безопасно, если оно явно обозначено и система продолжает читать поток. Оно опасно, когда следующий читатель не понимает, полное ли сообщение об ошибке перед ним, когда шлюз прекращает чтение и блокирует дочерний процесс или когда отброшенный вывод может содержать единственное описание действия, для которого нет другой записи.

Сохраняемый хвост должен начинаться с маркера, добавленного шлюзом, а не помощником. Например:

[stderr truncated: kept last 16384 of 184320 bytes]
connection retry 18 failed: remote side closed the channel

Этот маркер входит в запись, а не служит украшением. Он объясняет пользователю, почему первая видимая строка начинается резко, и не даёт агенту принять неполный стек вызовов за полное объяснение. Если вы сохраняете начало и хвост, укажите оба объёма в байтах. Никогда не соединяйте их молча.

Задавайте лимиты, исходя из параллельности, а не копируйте число из другого проекта. Спросите себя, сколько действий один запуск агента может выполнять одновременно, сколько запусков принимает хост, сколько памяти диагностика может потребить в тяжёлую минуту и какой объём текста человек действительно способен изучить. Последняя цифра обычно намного меньше, чем ожидают.

Храните результат действия отдельно от фрагмента вывода. Успешное HTTP-действие должно вернуть свой предусмотренный структурированный результат, даже если stderr достиг лимита, если только сам помощник не считает превышение признаком ошибки. И наоборот, отсутствие данных в stderr не доказывает успех действия. Считайте диагностику одним из нескольких наблюдений.

Тесты на поток вывода должны быть рядом с обычными интеграционными тестами

Не храните учётные данные в выводе помощника
Sallyport хранит API- и SSH-учётные данные в зашифрованном хранилище во время выполнения действия.

Помощник, который выдаёт слишком много stderr, не является редким случаем из области безопасности. Это базовый тест надёжности. После обновления библиотеки она может стать разговорчивой, удалённые клиенты начинают повторять предупреждения при повторных попытках, а некорректный ввод может запустить цикл ошибок. Если в тестах шлюз видит только тихих помощников на успешном пути, у него нет доказательств, что обработка процессов выдерживает нагрузку.

Начните с фикстуры, которая выдаёт заданный объём stderr, завершается с выбранным статусом и не выполняет внешнюю работу. В Unix-подобной системе эта команда создаёт намеренный поток вывода для локального тестового стенда:

yes helper-diagnostic 1\u003e\u00262

Запустите её с коротким крайним сроком. Ожидаемый результат не сводится к срабатыванию крайнего срока. Убедитесь, что шлюз сохраняет не больше заданного объёма, отмечает усечение, завершает дочерний процесс, читает потоки до их закрытия и записывает конечное событие жизненного цикла.

Затем добавьте сценарии, выявляющие ошибки порядка:

  • помощник пишет stderr до ожидания одобрения;
  • помощник пишет во время одобрения и сразу завершается после него;
  • помощник успешно выполняет действие, заполняет stderr и получает отмену во время очистки;
  • оболочка завершается, пока потомок сохраняет stderr открытым;
  • помощник выдаёт недопустимые последовательности байтов и одну строку длиннее сохраняемого лимита.

Для каждого сценария измеряйте небольшой набор фактов: пиковый объём сохранённой диагностики, время до одобрения, время от отмены до выхода процесса, финальное состояние и наличие ожидаемой записи вызова. Не ограничивайтесь тестом, который проверяет наличие слова «truncated» в строке ошибки. Такая строка может появиться, пока задача чтения остаётся заблокированной или финальная запись так и не попадает в хранилище.

Запускайте те же фикстуры с параллельными вызовами. Лимит на один вызов может выглядеть правильным при изолированной проверке и всё равно дать сбой, когда все вызовы одновременно достигают своих лимитов. Проверьте также отключение клиента. Процесс агента может исчезнуть, но хост всё равно должен завершить или отменить действие определённым образом.

Последняя проверка это автономная проверка цепочки аудита и сравнение с ожидаемым жизненным циклом фикстуры. Проверка может показать, изменялись ли сохранённые записи. Сравнение с фикстурой показывает, записал ли шлюз те события, которые должен был записать изначально. Нужны обе проверки.

Делайте логи полезными, но не позволяйте им управлять действием

Полезное правило простое: stdout передаёт сообщения протокола MCP, структурированные результаты передают итоги действий, а stderr содержит ограниченную диагностику. Когда у этих каналов разные задачи, одобрение, завершение, учёт памяти и записи аудита перестают спорить за один и тот же неструктурированный текст.

Начните с помощника, который уже доставлял вам неприятности: он слишком громко повторяет попытки, зависает после отмены или при сбое печатает весь удалённый ответ. Подключите его через читатель с ограничением и запускайте фикстуру потока вывода, пока запись действия не станет достоверной. Тихие логи приятны. Логи с ограниченным объёмом дают операционный контроль.

Вопросы и ответы

Действительно ли вывод stderr может остановить агента MCP?

Да. Если родительский процесс захватывает stderr без ограничения, помощник может превратить диагностический вывод в занятую память или заполнить канал, из-за чего помощник заблокируется. Сообщения MCP могут быть исправны, но процесс, который их передаёт, зависнет из-за собственных логов.

Куда сервер MCP должен писать логи, в stdout или stderr?

Не отправляйте логи в stdout. Транспорт stdio оставляет stdout для сообщений протокола, поэтому одна случайная диагностическая строка может повредить поток JSON-RPC. Отправляйте диагностику в stderr, а хост должен постоянно читать этот поток и ограничивать объём.

Какой лимит stderr задать для помощника агента?

Для начала задайте небольшой лимит в байтах на каждый вызов, ещё меньший объём сохраняемого хвоста и жёсткий общий лимит для процесса. Точные значения зависят от параллельности и доступной памяти, но лимит должен быть явным и наблюдаемым, а не случайным свойством среды выполнения хоста.

Безопасно ли усекать stderr помощника?

Сохраняйте ограниченный хвост, записывайте общее число полученных байтов и отмечайте усечение. Для полной записи аудита не нужна полная копия каждой строки отладки. Нужны честные сведения о том, что запускалось, что произошло и какие данные были отброшены.

Могут ли подробные логи задержать запрос на одобрение?

Одобрение должно зависеть от жизненного цикла действия, а не от объёма логов. Запускайте отсчёт, когда действие становится доступным для одобрения человеком, и продолжайте читать stderr, пока карточка видна.

Нужно ли одновременно читать stdout и stderr?

Всегда читайте оба потока параллельно. Чтение только stdout может заблокировать процесс, если он заполнит канал stderr. Для помощника, который выдаёт большой ответ, возможна и обратная ситуация.

Что должен записать журнал аудита, если помощник принудительно завершили?

Сначала запишите попытку действия, затем зафиксируйте, началась ли отправка, завершилась ли она и был ли вывод усечён. Если помощник пережил отключение клиента или клиент завершился первым, журнал должен сообщить об этом, а не создавать впечатление успешного завершения.

Защитит ли тайм-аут от переполнения логами?

Нет. Короткий тайм-аут ограничивает время, а лимит stderr ограничивает объём сохранённых данных и нагрузку на канал. Нужны оба ограничения, а также путь отмены, который закрывает потоки и ждёт завершения процесса.

Как тестировать шумных помощников MCP?

Используйте реального помощника, который выдаёт заданный объём stderr, и пропустите его через тот же шлюз и путь одобрения, что используются в рабочей среде. Измеряйте пик сохранённых байтов, время до одобрения, задержку отмены, код завершения и наличие каждой записи жизненного цикла.

Зачем шлюзу действий ограничивать логи подпроцессов?

Шлюз, выполняющий действия от имени агента, должен считать вывод помощника недоверенными входными данными. Он должен сохранять полезный хвост и диагностику, не позволяя агенту или ошибочной зависимости расходовать память или бесконечно удерживать действие открытым.

Sallyport

Sallyport выполняет API-вызовы и SSH-команды за вашего ИИ-агента. Ключи остаются в локальном хранилище на вашем Mac; вы подтверждаете каждый запуск, и каждое действие попадает в запечатанный журнал.

© 2026 Sallyport · Открытый код по лицензии Apache-2.0 · Oleg Sotnikov