Читать 8 мин

Сбрасывает ли пересборка MCP-сервера сеанс агента?

Разберите, когда пересборка MCP-сервера должна обновить метаданные инструментов, заменить идентификатор исполняемого кода и аннулировать авторизацию агента во время активного сеанса.

Сбрасывает ли пересборка MCP-сервера сеанс агента?

Локальная пересборка MCP-сервера не сводится к одному событию. Она может изменить каталог инструментов, который видит агент, заменить код, обрабатывающий вызовы, или изменить полномочия, одобренные человеком. Если считать все эти изменения одной общей «перезагрузкой», возникают две самые опасные проблемы: агент вызывает инструменты со старыми схемами или новый код получает разрешения, выданные старому коду.

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

У пересборки есть три отдельных последствия

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

Метаданные инструментов это описание, передаваемое через tools/list: имена и описания инструментов, схемы входных данных, при наличии схемы выходных данных и аннотации. На их основе агент и хост решают, доступен ли вызов и как его сформировать.

Идентификатор исполняемого кода отвечает на другой вопрос: какой код сейчас получит этот вызов? Для локального сервера ответ может включать скомпилированный бинарный файл, интерпретируемый файл запуска, lockfile, версию среды выполнения, дайджест образа контейнера или конфигурацию супервизора. Отображаемое имя вроде payments-dev не является идентификатором. Путь вроде /Users/dev/work/payments/dist/server.js тоже.

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

Команды смешивают эти границы, потому что цикл разработки часто выглядит как одно действие:

  1. Изменить обработчик.
  2. Собрать проект или сохранить файл.
  3. Перезапустить локальный сервер.
  4. Продолжить сеанс агента.

На самом деле этот цикл меняет несколько вещей. Обработчик может получить побочный эффект, не меняя имени или схемы. Схема может получить поле environment, хотя байты исполняемого файла пока не изменились. Супервизор может заменить дочерний процесс, пока родитель сохраняет открытое stdio-соединение. Если называть все это «горячей перезагрузкой», у вас не будет надежного правила, что именно хост должен аннулировать.

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

Метаданные нужно обновлять при изменении объявленного контракта

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

Спецификация MCP Tools предоставляет серверам notifications/tools/list_changed, чтобы сообщать клиентам об изменении списка инструментов. Это полезный, но намеренно небольшой механизм: уведомление сообщает, что список изменился, но не говорит, что именно изменилось и получил ли клиент список заново. Сервер, отправляющий уведомление, должен ожидать, что клиент снова выполнит tools/list. Клиенту не следует считать, что каждый сервер или хост отреагирует мгновенно, особенно в разных реализациях клиентов с агрессивным кэшированием.

Обновляйте метаданные при изменении любого из этих полей:

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

Последний пункт требует оценки. Схема MCP описывает аннотации как подсказки и предупреждает клиентов не принимать решения о безопасности на основе аннотаций, полученных от ненадежного сервера. Это правильное предупреждение. readOnlyHint может помочь хосту отобразить вызов, но не превратит запись в чтение. Если локальный сервер меняет readOnlyHint с true на false, обновите каталог, чтобы представление для человека оставалось достоверным. Не позволяйте этому полю решать, получит ли вызов учетные данные.

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

{
  "name": "publish_preview",
  "inputSchema": {
    "type": "object",
    "required": ["branch"],
    "properties": {
      "branch": { "type": "string" }
    },
    "additionalProperties": false
  }
}

Разработчик пересобирает его и добавляет необязательное поле:

"target": {
  "type": "string",
  "enum": ["preview", "production"],
  "default": "preview"
}

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

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

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

Изменившемуся исполняемому коду нужен новый идентификатор

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

Чаще всего я вижу ошибку, когда доверие привязывают к командной строке. Хост хранит что-то вроде этого:

server = "inventory"
command = "node"
args = ["/work/inventory/server.js"]

После этого он считает сервер неизменным, пока не поменяется конфигурация. Это неверно. После пересборки процесс node может загрузить другой server.js. Файл может разрешить другое дерево зависимостей. Он даже может остаться побайтно прежним, если другой нативный модуль, переменная среды или оболочка направляет выполнение в другое место.

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

{
  "serverLabel": "inventory-local",
  "launchCommand": ["node", "/work/inventory/dist/server.js"],
  "entryDigest": "sha256:9e4c...71af",
  "lockfileDigest": "sha256:344b...0d19",
  "runtime": "node 22.14.0",
  "workingDirectory": "/work/inventory",
  "epoch": "01JQ7R4S4S0QJ7GZP1S2",
  "processId": 48192
}

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

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

В macOS эта простая проверка дает разработчику повторяемую запись перед запуском локального сервера:

shasum -a 256 dist/server.js package-lock.json

Обычно в результате на каждой строке указаны один дайджест и один путь:

9e4c1b2d8f3a6d...71af  dist/server.js
344bb81b6c09de...0d19  package-lock.json

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

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

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

Авторизация должна следовать за эпизодом выполнения

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

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

Практическое разрешение может быть связано с такими полями:

{
  "agentRun": "run_01JQ7R1",
  "serverLabel": "inventory-local",
  "executionEpoch": "01JQ7R4S4S0QJ7GZP1S2",
  "toolScope": ["inventory_lookup", "inventory_adjust"],
  "credentialScope": ["inventory-api-staging"],
  "issuedAt": "2026-07-22T14:31:08Z",
  "expiresWhen": "agent-run-ends"
}

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

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

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

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

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

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

Stdio скрывает замену процесса за одним каналом

Храните секреты вне кода агента
Sallyport выполняет действия через HTTP и SSH, не раскрывая агенту учетные данные из хранилища.

Соединение MCP через stdio особенно обманчиво при пересборке, потому что соединение принадлежит процессам, а не файлам. Пересборка файла ничего не делает с работающим дочерним процессом, пока какой-либо компонент не заменит или не перезагрузит этот процесс.

В простом случае хост MCP запускает дочерний сервер и удерживает каналы его стандартного ввода и вывода. Дочерний процесс загрузил код при запуске. Разработчик снова запускает сборку, но существующий дочерний процесс остается в памяти. Агент по-прежнему разговаривает со старой реализацией, хотя в каталоге уже лежат новые артефакты.

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

build complete: dist/server.js changed
running server unchanged: pid=48192 epoch=01JQ7R4S4S0QJ7GZP1S2

Более сложный вариант использует наблюдатель. Родительский процесс владеет stdio-каналом, следит за файлами, завершает рабочий процесс и запускает новый. Родитель может сохранить канал открытым, пока вызовы незаметно начинают попадать к новому дочернему процессу. С точки зрения клиента MCP соединение не закрывалось. С точки зрения проверяющего безопасность идентификатор исполняемого кода изменился внутри существующего сеанса.

Не оставляйте такую замену незаметной. Выберите один из вариантов:

  1. Закрывайте MCP-соединение при перезапуске дочернего процесса, заставляя хост подключиться заново, выполнить инициализацию и авторизовать новый экземпляр.
  2. Сохраняйте внешнее соединение, но заставьте супервизор сообщать хосту новый эпизод выполнения до передачи любого нового вызова.
  3. Не используйте перезагрузку внутри процесса при разработке, а перезапускайте весь процесс сервера под управлением хоста.

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

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

Уведомления об изменении списка инструментов это подсказка, а не передача управления

notifications/tools/list_changed должно побуждать получить каталог заново, но оно не перезапускает сеанс, не согласует возможности заново и не передает решение об авторизации. Стройте поток управления вокруг фактического содержания уведомления.

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

Это различие важно при проектировании перезагрузки. Слабая реализация выглядит так:

watcher rebuilds server
server sends tools/list_changed
client fetches tools/list
agent continues

В демонстрации она работает. Но она не отвечает на четыре рабочих вопроса:

  • Загрузил ли существующий процесс пересобранный код?
  • Перехватил ли соединение другой процесс?
  • Имеет ли новый код те же одобренные полномочия?
  • Отбросил ли хост вызов, который агент подготовил по старой схеме?

Более надежный путь распределяет обязанности между уровнями. Система сборки сообщает об артефактах. Супервизор сообщает о замене процесса. MCP-сервер сообщает об изменениях каталога. Хост обновляет метаданные, видимые агенту. Слой авторизации сравнивает эпизод выполнения с разрешением. Журнал аудита записывает каждый переход.

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

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

Составьте контракт обновления до добавления горячей перезагрузки

Проверить цепочку аудита в автономном режиме
sp audit verify проверяет зашифрованную хеш-цепочку в автономном режиме, без ключа хранилища.

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

Используйте небольшую машину состояний. Ей не нужен язык политик или лабиринт правил.

ready(epoch A, catalog 12, approval A)
  build artifact changes
ready(epoch A, catalog 12, approval A)
  worker restarts
identity-pending(epoch B, catalog unknown, approval A invalid)
  host fetches tools/list
catalog-ready(epoch B, catalog 13, approval A invalid)
  reviewer approves required scope
ready(epoch B, catalog 13, approval B)

Ключевой переход это identity-pending. В этом состоянии хост не должен пропускать вызов с учетными данными лишь потому, что агент уже его подготовил. Можно разрешить безопасные запросы обнаружения, если у вас есть четкое определение безопасности, но не следует гадать. Для большинства локальных серверов проще приостанавливать все вызовы инструментов, пока каталог и разрешение не станут актуальными.

В контракте простым языком зафиксируйте следующие решения:

  • Компонент, создающий эпизод выполнения.
  • Артефакты и сведения о среде выполнения, входящие в идентификатор кода.
  • Изменения метаданных, требующие нового выполнения tools/list.
  • Области авторизации, истекающие при смене эпизода.
  • Поведение для вызова, находящегося в процессе перезапуска.

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

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

Проверяйте пересборки как изменения полномочий

Тест пересборки должен доказывать больше, чем «новый инструмент появился». Он должен показывать, что устаревшие метаданные не позволяют сформировать опасный вызов, старое разрешение не дает доступа к новому коду, а журнал аудита различает два эпизода выполнения.

Запустите этот тест в локальной среде с сервером, у которого есть один инструмент записи с учетными данными и один безопасный инструмент чтения.

  1. Запустите ревизию сервера A. Сохраните запись о выполнении, получите tools/list и разрешите одному запуску агента использовать инструмент записи.
  2. Один раз вызовите инструмент записи с маркером вроде revision=A. Убедитесь, что журнал содержит эпизод A и разрешение для эпизода A.
  3. Пересоберите ревизию B. Сохраните имя инструмента, но добавьте обязательное поле схемы или измените обработчик так, чтобы он записывал revision=B.
  4. Замените работающий процесс тем же способом, который используется при обычной разработке.
  5. Попробуйте выполнить старый подготовленный вызов до обновления метаданных и получения разрешения. Хост должен отклонить его, потому что эпизод изменился.
  6. Получите новый каталог, запросите новое разрешение, если вызову нужны полномочия, и повторите вызов. Убедитесь, что журнал содержит эпизод B и новое разрешение.

Ожидаемый отказ должен быть достаточно подробным для отладки:

{
  "error": "authorization_stale",
  "reason": "server execution epoch changed",
  "approvedEpoch": "01JQ7R4S4S0QJ7GZP1S2",
  "currentEpoch": "01JQ7R9KQ6K2Y8W4JH0M",
  "retry": "refresh tool metadata and request authorization"
}

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

Добавьте ошибки, которые разработчики часто не проверяют:

  • Сборка завершилась успешно, но старый процесс продолжает работать.
  • Процесс перезапустился, но список инструментов остался прежним.
  • Схема изменилась, но клиент игнорирует tools/list_changed.
  • Перезапуск произошел, пока вызов записи ожидал ответа.
  • Путь к серверу остался прежним, но изменилось разрешенное дерево зависимостей.

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

Записи аудита должны показывать, какой код выполнил действие

Отслеживать каждый внешний вызов
Журнал Activity записывает каждое отдельное действие в зашифрованный журнал аудита.

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

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

Компактная последовательность событий может выглядеть так:

{"type":"server_started","epoch":"01JQ7R4...","entryDigest":"sha256:9e4c...71af"}
{"type":"approval_granted","run":"run_01JQ7R1","epoch":"01JQ7R4...","scope":"inventory-api-staging"}
{"type":"tool_called","run":"run_01JQ7R1","epoch":"01JQ7R4...","tool":"inventory_adjust"}
{"type":"server_replaced","oldEpoch":"01JQ7R4...","newEpoch":"01JQ7R9..."}
{"type":"authorization_denied","run":"run_01JQ7R1","epoch":"01JQ7R9...","reason":"stale_epoch"}

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

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

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

Скорость пересборки не оправдывает унаследованное доверие

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

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

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

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

Требует ли пересборка MCP-сервера перезапуска агента?

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

Что делает tools/list_changed после пересборки MCP-сервера?

Используйте notifications/tools/list_changed, если изменился список инструментов, их описания или схемы. Воспринимайте это как просьбу клиенту получить свежие метаданные, а не как гарантию, что каждый клиент немедленно это сделает. Уведомление не создает новый идентификатор исполняемого файла и не продлевает авторизацию.

Нужно ли заново подтверждать пересобранный локальный MCP-сервер?

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

Достаточно ли пути к исполняемому файлу для идентификации MCP-сервера?

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

Что делать, если меняется только схема входных данных MCP-инструмента?

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

Можно ли безопасно использовать горячую перезагрузку MCP-сервера?

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

Согласует ли MCP возможности заново после пересборки сервера?

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

Можно ли сохранить то же имя MCP-инструмента после изменения его поведения?

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

Что должен записывать журнал аудита для пересобранных MCP-серверов?

Записывайте идентификатор запуска агента, идентификатор процесса сервера, дайджест исполняемого файла, эпизод сервера, дайджест метаданных инструмента, решение о разрешении и каждый вызов. Это позволяет определить, выполнялся ли вызов до пересборки или после нее. Журнал, в котором есть только имена инструментов, не даст ответа на этот вопрос.

Как шлюзу MCP обрабатывать пересобранный сервер?

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

Sallyport

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

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