Безсостоянийный MCP: масштабирование серверов без сессий и процедур обмена данными
Как протокол Model Context без состояния прерывает сессии и процедуры обмена данными, и как _метаданные, многокруговые запросы, заголовки маршрутизации, кэширование и задачи делают его пригодным для использования.
Сервер протокола Model Context Protocol, который работает без сбоев на одной машине, может начать выдавать ошибки сразу же при запуске трех копий за балансировщиком нагрузки, поскольку каждый экземпляр запоминает только те сессии, которые он сам создал. Переход протокола к безсостоянийной архитектуре направлен именно на решение этой проблемы. В этой статье объясняется, что меняется при отсутствии сессий и процедуры инициализации, как запросы передают свой собственный контекст, а также как многократные взаимодействия, маршрутизация на основе заголовков, кэшируемые списки и фоновые задачи вписываются в новую модель, чтобы вы могли оценить их влияние на серверы и шлюзы, которые вы используете.
Примечание по временным рамкам: описанный здесь дизайн без состояния относится к ревизии протокола 2026 года. Такие детали, как точные названия методов, заголовков и типов результатов, могут ещё измениться, поэтому перед использованием в коде убедитесь в их соответствии текущей спецификации MCP. Если вам нужно вспомнить основы того, как клиенты MCP находят и вызывают инструменты, введение к статье на блоге о том, как протокол Model Context Protocol позволяет агентам находить и вызывать инструменты, рассматривает эту тему.
Что здесь означает «состояние»
Система считается имеющей состояние, когда ей необходимо запоминать что-то между запросами для обработки следующего. Хорошим примером из повседневной жизни является заказ доставки еды: он проходит этапы от принятия заказа до подготовки, затем доставки и, наконец, выполнения заказа, причем сервис должен отслеживать, на каком этапе находится каждый заказ, чтобы в любой момент можно было ответить на вопрос «Где моя еда?».
Ранние версии MCP работали аналогичным образом на уровне соединения. Когда клиент (приложение ИИ) впервые подключался к серверу, они выполняли процедуру инициализации. Затем сервер выдавал идентификатор сессии, например abc123, и клиент прикреплял его ко всем последующим запросам, чтобы сервер мог связывать каждый вызов с тем, что происходило ранее в разговоре, включая возможности, согласованные обеими сторонами.
Почему сессии нарушаются при горизонтальном масштабировании
При наличии одного серверного инстанса и небольшом объеме трафика такая конфигурация вполне подходит. Проблемы начинаются, когда нагрузка растет и вы добавляете дополнительные инстансы за балансировщиком нагрузки:
- Клиент пользователя отправляет первый запрос, например, с просьбой предоставить список инструментов. Балансировщик нагрузки направляет его на Сервер 1, который создает сессию
abc123. - Тот же клиент отправляет второй запрос, например, с просьбой использовать инструмент для получения информации о погоде. На этот раз балансировщик нагрузки выбирает Сервер 2.
- Сервер 2 ранее не имел информации о
abc123. У него отсутствует любой внутренний состояние Сервера 1, поэтому запрос отклоняется.
У систем с состоянием существуют два стандартных решения. Механизм «липких сессий» привязывает каждого клиента к одному экземпляру, что мешает равномерному распределению нагрузки и усложняет процесс переключения на резервный сервер. В качестве альтернативы используется общий хранилище, такое как Redis, в котором хранятся данные сессий, доступные для чтения всеми экземплярами. Этот подход работает, но требует дополнительной инфраструктуры для её управления, защиты и обеспечения доступности, а также сетевых поисков при каждом запросе — всё это исключительно для поддержания протокольной отчетности.
Безсостоянийная модель
Версия 2026 года выбирает другой путь: она превращает MCP в безсостоянийный протокол запрос-ответ и отказывается от использования сессий. Каждый запрос рассматривается независимо и не зависит от того, что сервер запомнил из предыдущего запроса. Поскольку на сервере нет отдельной памяти для каждого клиента, любой экземпляр может обработать любой запрос, а масштабирование достигается путем добавления новых экземпляров за обычным балансировщиком нагрузки.
Это не означает, что приложение вообще не может иметь состояния. Инструменты для управления корзиной покупок или редактирования длинных документов всё равно нуждаются в хранении данных. Разница заключается в том, что такое состояние становится явными данными приложения, хранящимися в выбранном месте и обращаемыми по идентификаторам в запросе, в отличие от косвенного протокольного состояния, связанного с соединением.
Как запрос несёт свой собственный контекст
Даже без процедуры установления соединения или идентификатора сессии сервер всё равно должен знать, какую версию протокола использует клиент, кто этот клиент и какие у него возможности. Ответ заключается в том, что каждый запрос приносит с собой эту информацию.
Объект _meta
В телах запросов может присутствовать необязательный объект _meta для хранения таких метаданных. Он может содержать:
- Версию протокола, чтобы сервер знал, как интерпретировать сообщение.
MyAIApp v1.0.Поскольку контекст поступает в составе запроса, сервер может обработать его немедленно, без необходимости поиска в таблице сессий. Недостатком является незначительно больший объем данных при каждом вызове, что обычно незаметно по сравнению с затратами на использование общего хранилища сессий.
Многократные взаимодействия без открытого соединения
Дизайн с учетом состояния облегчает взаимодействие: если серверу нужна дополнительная информация, он может запросить ее по уже открытому соединению. Возьмем пользователя, который хочет забронировать билет в Дели, но забыл указать дату. Сервер с учетом состояния может просто попросить указать дату и дождаться ответа по тому же соединению.
Протокол без состояния не может поддерживать открытые соединения в этой цели, поэтому MCP определяет структурированный многокруговой процесс вместо этого:
- Когда сервер получает запрос, в котором отсутствуют необходимые параметры, он возвращает специальный результат
input required, вместо того чтобы выдать ошибку или ждать. - Приложение-клиент запрашивает у пользователя отсутствующие данные.
- Клиент помещает ответы в объект
input responsesи отправляет новый, полностью независимый запрос, который сервер может обработать.
Поскольку второй запрос содержит всё необходимое, он может попасть на любую инстанцию сервера. Серверу не нужно запоминать, что он задавал вопрос; клиент продолжает обработку запроса. Если серверу необходимо связать два запроса (например, чтобы избежать повторной выполнения ресурсоемких операций), он может вернуть клиенту непрозрачный токен, который тот должен вернуть обратно, вместо того чтобы хранить скрытую информацию.
Маршрутизация по заголовкам вместо тела запроса
Безсостоянийный подход также открывает возможности для улучшения производительности и операционных показателей. Выделяются два ключевых изменения: маршрутизация на основе заголовков и возможность кэширования результатов.
Подробности протокола в заголовках HTTP
Ранее инфраструктура, расположенная перед сервером MCP, такая как API-шлюз, брандмауэр веб-приложений или балансировщик нагрузки, должна была анализировать тело запроса в формате JSON, чтобы определить, какой метод или инструмент используется. Анализ тела запросов на уровне входа потребляет ресурсы процессора, увеличивает задержку и затрудняет настройку во многих шлюзах.
Согласно новым правилам, HTTP-запросы должны содержать ключевую информацию протокола в заголовках:
MCP-Method, напримерtools/call;MCP-Name— название конкретного инструмента, который вызывается.
Шлюз может читать эти заголовки и направлять трафик, ограничивать его скорость или блокировать без вмешательства в содержимое сообщения. Это позволяет просто формулировать распространённые правила — например, отправлять ресурсоемкие инструменты в специальный пул экземпляров, применять более строгие ограничения к определённому инструменту или полностью блокировать его во время инцидента. Как и всегда с заголовками, сервер должен проверять их соответствие содержимому, чтобы клиент не мог обойти правила, отправив вводящий в заблуждение заголовок.
Списки инструментов и промптов, подлежащих кэшированию
Клиенты постоянно задают серверам одни и те же вопросы: какие инструменты доступны, какие промпты поддерживаются. Учитывая тысячи пользователей, сервер может тратить значительную часть своих ресурсов на обработку этих одинаковых запросов.
Списки инструментов и промптов редко меняются, поэтому новая версия позволяет кешировать результаты этих списков. Клиент может загрузить список инструментов один раз, сохранить его в памяти и использовать заново при последующих запросах, вместо того чтобы запрашивать его снова. Благодаря этой же особенности общая инфраструктура, такая как шлюз или HTTP-кэш перед серверами, может одновременно обрабатывать множество повторных запросов на списки от разных клиентов. В любом случае к серверу поступает гораздо меньше запросов. Как и в любом кэше, необходим способ аннулирования его содержимого при изменении списка в результате обновления, поэтому следует планировать срок действия кэша или использование версионирования, а не хранение данных в кэше бесконечно.
Длительная обработка с использованием фоновых задач
Некоторые инструменты отвечают за миллисекунды, например при поиске погоды. Другие — нет: запрос к ассистенту с просьбой проанализировать 10 000 документов может занять двадцать минут. В обычном цикле запрос-ответ клиенту придётся поддерживать соединение открытым в течение всего времени, что занимает ресурсы с обеих сторон и заставляет интерфейс пользователя оставаться в состоянии ожидания.
Чтобы решить эту проблему, в версии 2026 года была внедрена переработанная схема Tasks:
- Создание. Когда клиент запускает ресурсоёмкий инструмент, сервер немедленно отвечает идентификатором задачи, например
task_abc123, после чего первоначальный запрос завершается. - Выполнение в фоновом режиме. Сервер выполняет анализ на фоне, пока пользователь продолжает работать с другими частями приложения.
Task Get.Task Update.Это знакомый асинхронный паттерн обработки задач из веб-API, применённый к MCP. Он обеспечивает отзывчивость приложений независимо от сложности выполняемых операций. При развертывании с несколькими инстансами помните, что статус задачи должен храниться в месте, доступном для всех инстансов, поскольку вызов Task Get может попасть на другой сервер, чем тот, который создал задачу. Протокол больше не требует совместного состояния сессии, но ответственность за создание долговечного хранилища задач всё равно лежит на вас.
Что это означает для ваших серверов
Если вы управляете или создаете серверы MCP, практический чек-лист выглядит следующим образом:
- Удалите скрытую память на уровне каждого соединения. Все, что инструменту нужно между вызовами, должно храниться в явном хранилище с использованием идентификаторов, отправляемых клиентом.
- Читайте контекст из каждого запроса. Берите версию протокола, идентификатор клиента и его возможности из поля
_meta, а не из сессии. - Проектируйте инструменты так, чтобы они запрашивали данные, а не ждали их. Возвращайте результат, требующий ввода данных, при их отсутствии, и ожидайте ответы в новом запросе.
- Используйте заголовки маршрутизации на уровне входа. Настройте шлюзы для маршрутизации и ограничения по параметрам
MCP-MethodиMCP-Name, а также проверяйте их согласованность с содержимым запроса на сервере.
Основные выводы
- Безсостоянийный подход заменяет сессионную архитектуру MCP независимыми запросами и ответами, избавляя от необходимости в «липких» сессиях или общем хранилище сессий для масштабирования.
- Хэндшейки и идентификаторы сессий отсутствуют; каждый запрос содержит версию протокола, идентификатор клиента и его возможности в поле
_meta. - Отсутствующая информация обрабатывается с помощью результата
input requiredи последующего запроса с даннымиinput responses, вместо поддержания открытого соединения. MCP-MethodиMCP-Name— это заголовки, которые позволяют шлюзам направлять трафик, ограничивать его скорость и блокировать без необходимости парсинга тел JSON.- Стабильные списки инструментов, промптов и ресурсов могут храниться в кэше, что снижает частую нагрузку на серверы.
- Инструменты с длительным временем выполнения сразу возвращают идентификатор задачи, после чего клиенты используют команды
Task GetиTask Updateдля получения дополнительной информации. - Безсостоянийный подход перемещает состояние вместо его полного устранения: данные приложения и прогресс выполнения задач всё равно требуют надежного хранилища, к которому может обращаться каждый экземпляр. Перед использованием убедитесь, что названия соответствуют текущей спецификации.
Связанные материалы
- Как протокол контекста модели позволяет ИИ-агентам находить и вызывать инструменты — понятное объяснение MCP: как хосты, клиенты и серверы помогают ИИ-приложениям находить инструменты, вызывать их с структурированными данными и в чём заключаются их ограничения.
- Проектирование эффективных с точки зрения токенов интерфейсов инструментов для серверов MCP с агентами — узнайте, как свести десятки определений инструментов MCP к нескольким интерфейсам, ориентированным на конкретные задачи и действия, не теряя при этом основных функций.