Главная / Статьи

MCP 2026-07-28: Как спецификация без состояния меняет дизайн удаленных серверов

Спецификация MCP 2026-07-28 переходит к режиму без состояния: отсутствуют сессии и процедура установления соединения, а также введены MRTR, задачи и подсказки кэша. Какие изменения касаются удаленных серверов и как их мигрировать.

Издатель: REACTAPP.TOP23 мин чтения5381 слов

Источники: MCP Just Changed Its Architecture: Why the 2026 Specification Makes MCP Truly Cloud-Native

Переломный момент для MCP

Обновление Model Context Protocol от 2026-07-28 представляет собой гораздо более значимое событие, чем простое обычное обновление версии. Оно опровергает одно из основополагающих предположений MCP, а именно то, что удаленный сервер организован вокруг сессии протокола, связанной с соединением. MCP отошел от модели с состоянием и двунаправленного взаимодействия к модели запросов и ответов без состояния, которая функционирует как обычная распределенная инфраструктура HTTP. В объявлении о кандидатской версии говорилось, что это самое масштабное обновление с момента запуска протокола, а окончательная спецификация была опубликована 28 июля 2026 года.

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

В новом дизайне устраняется сессия протокола, а также удаляются операции handshake initialize и initialized. Каждый запрос содержит необходимые метаданные клиента, а серверы становятся доступными через явное обнаружение. Взаимодействие от сервера к клиенту реорганизуется в виде многократных запросов, HTTP-заголовки передают метаданные маршрутизации, клиенты получают подсказки по кэшированию в ответах на запросы к спискам и ресурсам, длительные операции переносятся в расширение Tasks, управление жизненным циклом расширений формализуется, а вводится более строгая политика устаревания. В результате получается ядро MCP, которое легко интегрируется с обычными балансировщиками нагрузки, шлюзами, брандмауэрами веб-приложений, а также с безсерверными или автомасштабируемыми решениями.

Один умственный модель помогает. Старая версия удаленного MCP функционировала как протокол распределенного диалогового соединения, тогда как новая версия работает как HTTP API, чье состояние явно определяется на уровне приложения или расширения.

Почему MCP, основанный на сессиях, оказался неэффективным

Фразу «состоянийный MCP перестал работать» необходимо внимательно читать, ведь состоянийный MCP не перестал функционировать внезапно. Произошло следующее: состояние, управляемое протоколом, стало все меньше соответствовать инфраструктуре, в которой размещались серверы MCP.

В более ранней версии клиент инициировал соединение с помощью запроса initialize, а сервер отвечал значением Mcp-Session-Id, которое должен был содержать каждый последующий запрос. Это связывало логического клиента с сессией на стороне сервера. Примеры до и после, приведённые самими разработчиками, наглядно показывают разницу: в старом подходе требовался запрос на инициализацию перед фактическим вызовом инструмента, тогда как протокол 2026 позволяет отправлять вызов инструмента непосредственно в виде одного самостоятельного запроса.

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

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

Хранение сессий в общем хранилище решает часть проблемы, но одновременно создаёт проблемы, связанные с распределёнными системами. Каждая сессия превращается в запись, которую необходимо создавать, реплицировать, аннулировать срок действия, удалять и постоянно держать в доступности. В этом случае сервер MCP представляет собой приложение, включающее в себя подсистему управления состоянием сессий, распределённую по нескольким машинам. В версии-кандидате на выпуск «липкие сессии» и общие хранилища рассматриваются как инфраструктура, которая больше не требуется на уровне протокола после исчезновения сессий.

Kubernetes, платформы автомасштабирования контейнеров, облачные функции и безсерверные среды выполнения делают эту проблему ещё более очевидной. Запрос может попасть на под A, этот под может исчезнуть во время масштабирования, а следующий запрос придёт на под F. Для безсостоянийного HTTP-сервиса это совершенно нормально, но это мешает протоколу, корректность которого предполагает наличие состояния, связанного с соединением.

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

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

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

Сессия и процедура установления соединения исчезли

Первое изменение, которое замечают пользователи, — это исчезновение сессии протокола. В дату 2026-07-28 исчезают операции initialize и обмен данными в роуте notifications/initialized, а также заголовок Mcp-Session-Id. Теперь каждый запрос содержит версию протокола и возможности клиента внутри поля _meta. Клиент, желающий заранее узнать информацию о сервере, может вызвать метод server/discover. В окончательном списке изменений оба этих исключения — сессии и процедуры установления соединения — отнесены к крупным изменениям.

Ранее последовательность действий включала процедуру установления соединения, за которой следовал сам запрос:

Client
  |
  | initialize
  v
MCP Server
  |
  | Mcp-Session-Id = ABC
  v
Client
  |
  | tools/call + Mcp-Session-Id: ABC
  v
Same logical server session

Теперь звонок может произойти напрямую:

Client
  |
  | tools/call
  | protocol version
  | capabilities
  | client metadata
  v
Load Balancer
  |
  +----> MCP Instance A
  |
  +----> MCP Instance B
  |
  +----> MCP Instance C

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

POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json

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

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

{
  "basket_id": "bsk_8f2a..."
}

а следующий вызов инструмента вернёт этот хендл вместе с элементом:

{
  "basket_id": "bsk_8f2a...",
  "item_id": "SKU-123"
}

На стороне сервера идентификатор ищется в Redis, PostgreSQL, DynamoDB, Cosmos DB, хранилище объектов или любой другой подходящей системе. Транспорт MCP больше не управляет этим состоянием. Авторы рекомендуют использовать явные идентификаторы, такие как basket_id и browser_id, в качестве способа замены скрытого протокольного состояния.

Это приводит к архитектурному разделению, показанному здесь:

MCP protocol state
        |
        v
       NONEApplication state
        |
        v
Explicit IDs + external state store


Long-running task state
        |
        v
Tasks extension + durable task store

Это, пожалуй, самая важная идея всего выпуска.

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

Если процедура установления соединения отсутствует, как серверу узнать, какую версию протокола и какие возможности поддерживает клиент? Ответ — метаданные по запросу. Зарезервированные поля _meta находятся в пространстве имён io.modelcontextprotocol/*, причём версия протокола и возможности клиента передаются с каждым запросом, без необходимости однократных переговоров, которые бы соединение тихо запоминало. Когда клиент решает идентифицировать себя, его идентичность также передаётся в виде метаданных по запросу, а серверы могут включать свои данные в метаданные результата. Концептуально запрос выглядит следующим образом:

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "search",
    "arguments": {
      "query": "MCP stateless architecture"
    },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {
        "extensions": {
          "io.modelcontextprotocol/tasks": {}
        }
      },
      "io.modelcontextprotocol/clientInfo": {
        "name": "my-agent",
        "version": "3.2.0"
      }
    }
  }
}

Когда предварительная передача информации полезна, сервер реализует server/discover. В окончательной версии changelog это становится обязательным RPC-запросом для серверов, что позволяет клиентам узнавать поддерживаемые версии протокола, возможности и идентификатор сервера перед выполнением любых других действий. Это удобно, когда клиенту необходимо выбрать своё поведение в зависимости от того, что поддерживает сервер.

Здесь существует тонкое различие: клиенты могут решить не вызывать server/discover, но серверы обязаны его предоставлять. Поэтому MCP больше не требует процедуры обнаружения для установления сессии, при этом он по-прежнему предлагает формальный способ проактивной проверки возможностей. Такая архитектура напоминает зрелые HTTP-API, где каждый запрос может быть интерпретирован независимо, а необязательные точки обнаружения или метаданные предоставляют более подробную информацию.

Маршрутизация по принципу круговой очереди и масштабирование до нуля

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

Возьмем три реплики, расположенные за балансиром нагрузки:

                  +------------------+
                  |  Load Balancer   |
                  +---------+--------+
                            |
               +------------+------------+
               |            |            |
               v            v            v
           MCP Pod A    MCP Pod B    MCP Pod C

Запрос 1 может попасть к A, запрос 2 — к C, а запрос 3 — к B. Никакое правило протокола не требует, чтобы все три запроса достигли одного и того же экземпляра, что именно и нужно облачным системам.

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

Сам сервис MCP является безсостоянийным, а бизнес-состояние сохраняется внешне от него.

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

Для реального сброса к нулю необходимо, чтобы все элементы, которые должны сохраняться после остановки процесса, находились в надежной инфраструктуре. Один из возможных вариантов производственной архитектуры предусматривает использование Redis для кратковременной координации, PostgreSQL или управляемой NoSQL-базы данных для хранения постоянного состояния приложения, а также хранилища объектов для больших файлов. Аутентификация также осуществляется вне процесса — с помощью систем авторизации OAuth или OIDC, а не на основе данных из локальной памяти.

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

Управление состоянием самостоятельно

Явные идентификаторы могут стать наиболее значимым результатом этой модели. Возьмём агента для закупок на основе ИИ, которому поручено заказать 100 GPU, получить одобрение и отправить заказ. Дизайн, основанный на сессиях, может легко скрыть такой процесс в состоянии сессии MCP. Новая архитектура способствует более видимому формату, при котором создание запроса возвращает идентификатор:

create_purchase_request()
        |
        v
purchase_request_id = pr_123
        |
        v
request_approval(pr_123)
        |
        v
approval_id = appr_789
        |
        v
submit_purchase(pr_123, appr_789)

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

{
  "purchase_request_id": "pr_123",
  "user_id": "user_42",
  "status": "awaiting_approval",
  "items": [
    {
      "sku": "GPU-H100",
      "quantity": 100
    }
  ],
  "created_at": "2026-08-21T08:00:00Z"
}

Сервер MCP просто разрешает идентификатор и проверяет авторизацию. Это также улучшает возможности отслеживания. Вместо фразы «сессия ABC совершила действие» система может сообщить о том, что запрос на покупку pr_123 был переведен из состояния draft в состояние approved — это имеет реальное бизнес-значение.

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

Многократные запросы

Отказ от постоянных запросов от сервера к клиенту создаёт ещё одну основную проблему. Рассмотрим вызов инструмента вроде этого:

delete_customer_data(customer_id=123)

Сервер не может безопасно удалять что-либо до тех пор, пока пользователь не подтвердит действие сопроводительным сообщением вроде «Удалить 48 записей?». В старом подходе канал может оставаться открытым, и от клиента могут запрашиваться дополнительные данные. Безсостоянийный дизайн не может полагаться на постоянно открытое соединение, и для устранения этой проблемы существуют многократные запросы с обратным ходом, или MRTR.

MRTR охватывает одну логическую операцию, которая требует нескольких обменов данными без постоянного соединения или сессии на стороне сервера. Во время ожидания подтверждения, уточнений или получения примеров данных ничто не удерживает HTTP-соединение открытым и не использует режим «липкого» соединения. Вместо этого взаимодействие представляет собой цепочку отдельных HTTP-запросов, связанных между собой состоянием продолжения выполнения. Клиент сначала запрашивает выполнение действия, например tools/call для инструмента удаления данных. Сервер в это время не запускает собственный RPC-запрос к клиенту, пока первоначальная операция находится в состоянии приостановки, и отвечает промежуточным результатом, указывающим на необходимость дополнительных данных:

{
  "resultType": "input_required",
  "inputRequests": {
    "confirm": {
      "type": "elicitation",
      "message": "Delete 48 records?",
      "schema": {
        "type": "boolean"
      }
    }
  },
  "requestState": "..."
}

Клиент собирает ответы и повторно отправляет первоначальный запрос с их приложением:

Client
   |
   | tools/call
   v
Server
   |
   | input_required
   | requestState
   v
Client
   |
   | user confirmation
   v
Client
   |
   | same operation + inputResponses + requestState
   v
Any MCP instance

Таким образом, операция разбивается на несколько HTTP-запросов и ответов, при этом нет необходимости поддерживать открытую транспортную связь. В официальной документации MRTR описывается как замена серверно-инициируемым шаблонам elicitation/create, sampling/createMessage и roots/list.

Ключевым полем является requestState. В него помещается всё, что необходимо серверу для возобновления работы, в виде зашифрованного или иным образом защищённого от изменений токена. Спецификация не требует хранения всего состояния приложения в одном центральном хранилище MCP — информация, нужная для продолжения взаимодействия, просто передаётся в следующем запросе. Это похоже на токен продолжения, причём большим преимуществом является то, что длительное логическое взаимодействие больше не означает длительное физическое соединение.

HTTP-заголовки и подсказки кэша

Переход на безсостоянийный формат сам по себе повышает масштабируемость, к тому же протокол становится более удобным для работы инфраструктуры HTTP. Запросы HTTP в формате потоковой передачи содержат стандартизированные заголовки, такие как Mcp-Method и Mcp-Name, что позволяет шлюзам и балансировщикам нагрузки принимать решения по маршрутизации, авторизации, ограничениям скорости и сбору телеметрии без необходимости обрабатывать тело сообщения в формате JSON-RPC. Тело сообщения по-прежнему является источником достоверной информации, и любое несоответствие между заголовком и соответствующим полем тела приводит к отклонению запроса.

Шлюз может считывать эти два значения:

Mcp-Method = tools/call
Mcp-Name   = search

и применять проверки политики такого рода:

tools/call + search  -> route/search-cluster
tools/call + execute -> route/execution-cluster
resources/read       -> route/resource-cluster

Это обеспечивает чёткое разделение плоскости управления и плоскости данных, поскольку посредник может определить операционную категорию запроса, не понимая JSON-схему приложения.

В этой версии также добавляются метаданные кэширования с помощью ttlMs и cacheScope. Ответ с списком может содержать их вот так:

{
  "result": {
    "tools": [
      ...
    ],
    "ttlMs": 60000,
    "cacheScope": "public"
  }
}

Семантика намеренно напоминает механизм HTTP cache-control. Параметр ttlMs указывает клиенту, как долго результат считается свежим, а cacheScope определяет, можно ли безопасно делиться этим результатом. В окончательной спецификации "public" отделяется от "private", причем разработчикам рекомендуется не использовать параметр cacheScope в качестве замены проверкам авторизации. Критерий свежести определяется следующим образом:

fresh_until = response_received_time + ttlMs

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

Каталоги инструментов делают это значение очевидным. Если сервер перечисляет 250 инструментов, а подключается 500 агентов, постоянные вызовы tools/list от каждого из них приводят к значительному объёму трафика управления. Разумное значение TTL позволяет клиентам повторно использовать каталог, а детерминированное порядок обработки предотвращает ненужные изменения в сериализованном наборе инструментов, что может сохранять стабильность кэшей вводных данных для больших языковых моделей. Именно по этой причине в чанглоге рекомендуется детерминированное порядок. Ещё одно преимущество: соединения SSE, оставшиеся открытыми, больше не являются единственным способом узнать о изменениях в списке, поэтому одновременно могут функционировать как механизмы обновления кэша, так и уведомления об изменениях.

Задачи с длительным временем выполнения

API без состояния для запросов и ответов по-прежнему должны обрабатывать задачи, которые выполняются в течение минут или часов. Возьмем, к примеру, такой звонок:

run_ml_training_job()

Он может отправить задание на распределенную обучающую обработку, дождаться GPU, выполнить несколько этапов, сгенерировать точки контроля и загрузить результаты. Поддержание открытого HTTP-соединения на протяжении всего этого процесса является хрупким решением. Архитектура 2026-07-28 решает эту проблему с помощью расширения Tasks, обозначаемого как io.modelcontextprotocol/tasks.

Для поддерживаемого варианта tools/call сервер может ответить с идентификатором задания вместо окончательного результата:

{
  "resultType": "task",
  "task": {
    "taskId": "task_7b93...",
    "status": "working"
  }
}

Затем клиент может запросить статус:

tasks/get(taskId)

Продолжайте запрашивать статус, пока задание не будет иметь значение completed, failed или cancelled. Расширение также предоставляет функцию tasks/update для ввода дополнительных данных и tasks/cancel для отмены задания.

Одно различие имеет критическое значение. Основной протокол является безсостоянийным, тогда как расширение Tasks намеренно обеспечивает сохранение состояния:

Core MCP
    |
    +---- Stateless request/response
Tasks extension
    |
    +---- Durable asynchronous state machine

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

MCP tools/call
      |
      v
Create task
      |
      v
Queue / workflow engine
      |
      +--> Kubernetes Job
      |
      +--> Azure Batch
      |
      +--> AWS Step Functions
      |
      +--> Databricks Job
      |
      +--> CI/CD pipeline

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

Осталась одна тонкость реализации. Расширение Tasks всё ещё может требовать, чтобы запросы направлялись в ту систему, которая хранит состояние задачи. В окончательном предложении по реализации Mcp-Name может содержать идентификатор задачи для операций tasks/get, tasks/update и tasks/cancel, что позволяет посредникам при необходимости отправлять дополнительные запросы в систему, хранящую состояние. Полная последовательность взаимодействия между клиентом, сервером MCP и движком задач показана на этой диаграмме последовательностей:

Client                  MCP Server               Task DB / Engine
  |                         |                           |
  |--- 1. tools/call ------>|                           |
  |                         |--- 2. Register Task ----->| (Status: working)
  |<-- 3. Return taskId ----|                           |
  |                         |                           |
  |--- 4. tasks/get ------->|                           |
  |                           \--- 5. Query state ----->| (Status: working)
  |<-- 6. Status: working --/                           |
  |                         |                           |
  |--- 7. tasks/get ------->|                           |
  |                           \--- 8. Query state ----->| (Status: completed)
  |<-- 9. Final Result -----/                           |

В типичном сценарии клиент запускает задачу с помощью tools/call, после чего сервер записывает информацию о задаче в общедоступный бэкенд, такой как Redis, PostgreSQL или движок рабочих процессов вроде AWS Step Functions или Kubernetes. Затем сервер сразу же возвращает taskId и начальный статус working. Первое HTTP-соединение закрывается корректно, освобождая сетевые ресурсы, а клиент периодически проверяет ход выполнения задачи с помощью tasks/get.

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

Агент для обработки данных в производстве и задач машинного обучения

Представьте производственную платформу ИИ, предоставляющую инструменты MCP агенту предприятия. У этого агента есть такие инструменты:

search_dataset()
inspect_schema()
run_sql()
start_training_job()
get_training_metrics()
deploy_model()
rollback_deployment()

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

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

search_dataset("customer churn latest")

и сервер возвращает идентификатор набора данных:

{
  "dataset_id": "ds_2026_08_20_0042"
}

Следующий запрос проверяет схему по этому идентификатору:

inspect_schema("ds_2026_08_20_0042")

Схема получается из системы метаданных. Следующий запрос запускает процесс обучения:

start_training_job("ds_2026_08_20_0042")

Сервер MCP создаёт долговечную задачу:

task_id = "task_train_98af..."

Само обучение происходит вне процесса MCP. Клиент запрашивает его состояние:

tasks/get("task_train_98af...")

И задача может проходить через ряд состояний:

QUEUED
   |
RUNNING
   |
EVALUATING
   |
INPUT_REQUIRED
   |
RUNNING
   |
COMPLETED

В состоянии INPUT_REQUIRED система может запросить одобрение на развертывание, отметив, что кандидат на 7,2% лучше, но приведёт к увеличению расходов на GPU на 18%. MRTR может задать этот вопрос без необходимости поддерживать открытое первое соединение. Клиент отвечает следующим образом:

inputResponses = {
    "approve": true
}

и запрос возобновляется.

Теперь представьте, что сервер MCP работает на шести репликах Kubernetes:

                  Ingress
                      |
              Load Balancer
                      |
        +------+------+------+------+
        |      |      |      |      |
       MCP1   MCP2   MCP3   MCP4   MCP5
                      |
                      v
               Task Store
                      |
              +-------+-------+
              |               |
           Redis          PostgreSQL
              |
              v
        Workflow Engine
              |
        +-----+------+
        |            |
     GPU Job     Model Registry

Здесь первый запрос tools/call направляется на MCP2, следующий — на MCP5, запрос на проверку статуса задачи — на MCP1, а попытка повторной отправки запроса на одобрение — на MCP3. Ничего не ломается, поскольку протокол больше не предполагает, что сессия связана с каким-либо конкретным сервером. Если сервер MCP3 выходит из строя, его заменяет Kubernetes. Если количество копий приложения сокращается с шести до двух за ночь, рабочий процесс продолжает работать, поскольку состояние задач и приложения хранится вне процесса. Именно здесь спецификация демонстрирует свою практическую ценность.

Авторизация и усиление безопасности

Помимо безсостоянийных изменений происходит значимое улучшение уровня безопасности. В финальной версии предусмотрены более строгие требования к работе OAuth и OIDC: серверы авторизации должны предоставлять параметр iss в соответствии с описанием в RFC 9207, а клиенты MCP обязаны проверять этот параметр перед использованием кода авторизации, тем самым предотвращая атаки, связанные с путаницей серверов авторизации. Кроме того, учетные данные клиента привязаны к серверу, который их создал, поэтому их повторное использование с другим сервером авторизации запрещено. Функция динамической регистрации клиентов сохраняется ради совместимости, но формально считается устаревшей; в качестве замены используются документы метаданных Client ID.

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

basket_id = bsk_123
user_id   = user_456

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

authenticated_subject == basket.owner

Затем применяются разрешения, специфичные для инструмента.

Процесс распространения контекста отслеживания W3C также задокументирован; он передаётся через поля _meta, traceparent, tracestate и baggage. Отслеживание действий в рамках хоста, клиента MCP, сервера MCP и вспомогательных сервисов становится гораздо проще, причём разработчики напрямую связывают это с трекингом, совместимым с OpenTelemetry. Поэтому возможности наблюдаемости должны быть частью проектирования с самого начала. Путь запроса, прошедший через систему отслеживания, может выглядеть следующим образом:

User Request
    |
    v
Agent
    |
    | traceparent
    v
MCP Client
    |
    | traceparent
    v
MCP Gateway
    |
    v
MCP Server
    |
    v
Database / Queue / API

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

Расширения получают статус первого класса

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

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

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

                    MCP
                     |
          +----------+----------+
          |                     |
       Core Protocol        Extensions
          |                     |
      Stateless HTTP      +-----+------+
                          |            |
                       Tasks       MCP Apps

Это предоставляет MCP гораздо более устойчивый способ развития.

Устаревание и обновление

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

Функции Roots, Sampling и Logging включены в список устаревших. Официальные рекомендации по миграции предусматривают замену Roots на явные параметры инструментов, URI ресурсов или настройки сервера, замену Sampling на прямую интеграцию с поставщиками LLM, а замену Logging на stderr или OpenTelemetry. Также устаревает старый способ передачи данных HTTP+SSE; предпочтительным вариантом является Streamable HTTP.

Экспериментальный API задач от 2025-11-25 обозначает ещё одну границу миграции. Теперь он находится в расширении io.modelcontextprotocol/tasks с переработанной жизненной циклом, основанной на методах tasks/get, tasks/update и tasks/cancel. Метод tasks/list устранён, поскольку отображение задач без сессии вызывает проблемы с областью действия и конфиденциальностью.

Для организации, мигрирующей существующий флот MCP, наиболее безопасный порядок действий выглядит следующим образом:

  1. Обновите SDK, затем найдите код, зависящий от Mcp-Session-Id. Внимательно изучите словари, ключи которых связаны с сессиями, реестры подключений и правила маршрутизации «липких» сессий, созданные на основе старой модели.
  2. Отделите состояние протокола от состояния приложения, присвоив всему, что представляет собой реальное бизнес-состояние, явный идентификатор и переместив его в подходящий по надёжности хранилище.
  • Обновите конфигурацию шлюза, чтобы понимать новые заголовки MCP, и укажите для кэшируемых ответов список явных политик TTL и диапазона, особенно учитывая, что кэширование типа public и private имеет серьезные последствия для безопасности.
  • Оцените возможность использования долгосрочно выполняющихся рабочих процессов вместо реализации задач в виде бесконечно открытых HTTP-запросов.
  • Документация по миграции на текущее TypeScript SDK также отражает переходный период. Реализации могут поддерживать новый протокол, сохраняя при этом механизмы совместимости с клиентами предыдущей версии, что позволяет осуществлять постепенную миграцию без необходимости мгновенного перехода на новую систему во всей экосистеме.

    Что это означает для архитектуры MCP

    Самым значительным последствием является то, что теперь MCP разделяет четыре аспекта, которые раньше смешивались:

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

    Структура слоёв выглядит следующим образом:

                     +----------------------------+
                     |        MCP Protocol        |
                     | JSON-RPC + request model   |
                     +-------------+--------------+
                                   |
                     +-------------v--------------+
                     |         Transport          |
                     | stdio / Streamable HTTP    |
                     +-------------+--------------+
                                   |
                     +-------------v--------------+
                     |      Application Layer     |
                     | explicit IDs + databases   |
                     +-------------+--------------+
                                   |
                     +-------------v--------------+
                     |         Extensions         |
                     | Tasks / MCP Apps / others   |
                     +----------------------------+
    

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

    Компромиссы

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

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

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

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

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

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

    Концептуальный сдвиг

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

    У транзакций есть идентификаторы транзакций, у рабочих процессов Kubernetes — идентификаторы задач, у корзин покупок — идентификаторы корзин, у CI-пайплайнов — идентификаторы запусков, а у задач обучения — идентификаторы выполнения. Теперь приложения MCP могут использовать один и тот же набор терминов.

    Что добавляет это обновление

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

    • Удаление Mcp-Session-Id прекращает связь на уровне протокола.
    • Отказ от процедуры подтверждения соединения делает каждый запрос автономной операцией обмена данными.
    • server/discover позволяет находить возможности без использования семантики сессий.
    • Mcp-Method и Mcp-Name обеспечивают совместимость трафика MCP с обычными инструментами HTTP.
    • ttlMs и cacheScope предоставляют клиентам и шлюзам общий язык для определения свежести данных и их обмена.
    • MRTR отказывается от идеи о том, что запрос сервера к клиенту требует постоянно действующего соединения, которое никогда не закрывается.
    • Задачи представляют собой устойчивую абстракцию для операций, длительность которых превышает срок одного HTTP-запроса.
    • Явные хэндлы выносят состояние бизнеса из скрытых транспортных метаданных в собственную модель данных приложения.
    • Расширения предотвращают увеличение размера ядра, позволяя экосистеме развиваться быстро.
    • Более строгая авторизация соответствует поведению реальных реализаций OAuth и OIDC.
    • Конвенции контекста трейсинга из OpenTelemetry облегчают отслеживание распределённых систем MCP.
    • Формальная политика устаревания предоставляет экосистеме предсказуемый способ изменений.

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

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

    sticky sessions
          +
    shared session store
          +
    long-lived connections
          +
    connection affinity
    

    и к этому:

    stateless request handling
          +
    ordinary load balancing
          +
    external durable state
          +
    explicit handles
          +
    asynchronous task primitives
    

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