MCP Docs ревизия 2026-07-28

Начало / Протокол / Транспорты

Транспорты

stdio и Streamable HTTP

Перевод и пересказОснова: Спецификация, раздел «Streamable HTTP»Сверено 05.10.2026, ревизия 2026-07-28Открыть .md

MCP поддерживает два транспорта. Формат сообщений JSON-RPC 2.0 у них одинаков, различается способ доставки.

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

Два транспорта#

Транспорт Как работает Когда подходит
stdio Потоки стандартного ввода и вывода между локальными процессами на одной машине. Без сетевых издержек. Локальные серверы, которые обычно обслуживают одного клиента.
Streamable HTTP HTTP POST для сообщений клиента, ответ обычным JSON или потоком Server-Sent Events. Поддерживает стандартные способы HTTP-аутентификации: токены предъявителя, ключи API, собственные заголовки. MCP рекомендует OAuth. Удалённые серверы, которые обслуживают многих клиентов.

На stdio клиентам НЕ СЛЕДУЕТ считать отдельную задачу, поток или разговор границей времени жизни процесса. Уведомление notifications/cancelled в базовом протоколе используется только на stdio. Проба совместимости для stdio описана на странице «Версии и совместимость».

Streamable HTTP#

Транспорт Streamable HTTP появился в версии 2025-03-26 вместо HTTP+SSE версии 2024-11-05. Ревизия 2026-07-28 изменила его поведение: убрана конечная точка GET для потока и отменены сессии на уровне протокола.

Сервер работает как независимый процесс, который обслуживает много клиентов:

  • у сервера одна конечная точка HTTP (например, https://example.com/mcp), которая принимает POST;
  • каждый запрос или уведомление клиента — отдельный HTTP POST;
  • на каждый запрос сервер отвечает либо одним объектом JSON, либо потоком Server-Sent Events, привязанным к этому запросу: в нём уведомления по запросу и итоговый ответ;
  • взаимодействия «сервер — клиент» (sampling, elicitation, roots) встроены в результаты как запросы входных данных по шаблону MRTR;
  • долгие уведомления об изменениях идут в потоке ответа на subscriptions/listen.

Безопасность и конечная точка#

  1. Серверы ДОЛЖНЫ проверять заголовок Origin у всех входящих соединений, чтобы предотвратить атаки через DNS rebinding. Если Origin есть и недопустим, сервер ДОЛЖЕН ответить 403 Forbidden.
  2. При локальной работе серверам СЛЕДУЕТ слушать только localhost (127.0.0.1), а не все сетевые интерфейсы (0.0.0.0).
  3. Серверам СЛЕДУЕТ реализовать аутентификацию для всех соединений.

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

Отправка сообщений#

  1. Клиент ДОЛЖЕН использовать HTTP POST.
  2. Клиент ДОЛЖЕН указать в заголовке Accept и application/json, и text/event-stream.
  3. Клиент ДОЛЖЕН включить заголовки метаданных запроса в каждый POST.
  4. Тело POST ДОЛЖНО быть одним запросом или уведомлением JSON-RPC. Клиент НЕ ДОЛЖЕН отправлять ответы JSON-RPC.
  5. На уведомление сервер, если принимает его, ДОЛЖЕН вернуть 202 Accepted без тела, а если не может принять, то код ошибки HTTP, например 400.
  6. На запрос сервер ДОЛЖЕН вернуть Content-Type: application/json или Content-Type: text/event-stream. Клиент ДОЛЖЕН поддерживать оба.

В базовой ревизии нет уведомлений «клиент — сервер» по Streamable HTTP: notifications/cancelled используется только на stdio.

Получение сообщений (поток SSE)#

  • Сервер МОЖЕТ присылать уведомления (например, notifications/progress или notifications/message) до итогового ответа, и они ДОЛЖНЫ относиться к исходному запросу.
  • Сервер НЕ ДОЛЖЕН присылать в этом потоке собственные запросы JSON-RPC. Взаимодействия «сервер — клиент» встраиваются в InputRequiredResult. Это изменение относительно версий 2025-03-26–2025-11-25, где сервер мог слать такие запросы по потокам SSE.
  • Поток СЛЕДУЕТ закрывать после итогового ответа.
  • Начиная поток, серверам СЛЕДУЕТ добавлять заголовок X-Accel-Buffering: no, чтобы обратные прокси (например, nginx) не буферизовали ответ.
  • Для долгих потоков, в частности subscriptions/listen, рекомендуется периодически посылать строку-комментарий SSE (начинается с двоеточия) как «пульс», чтобы промежуточные узлы не закрывали соединение. Клиенты ДОЛЖНЫ игнорировать такие строки.
  • Возобновление потоков по Last-Event-ID не поддерживается.

Отмена#

Закрытие потока ответа SSE сервер ДОЛЖЕН считать отменой этого запроса: прекратить работу как можно скорее и больше не присылать сообщений по нему.

Заголовки метаданных запроса#

Streamable HTTP дублирует часть полей тела JSON-RPC в заголовках HTTP, чтобы посредники (балансировщики, шлюзы, наблюдение) могли маршрутизировать и проверять запросы, не разбирая тело.

Заголовок версии протокола

Каждый POST на конечную точку ДОЛЖЕН содержать заголовок MCP-Protocol-Version, например MCP-Protocol-Version: 2026-07-28. Значение ДОЛЖНО совпадать с io.modelcontextprotocol/protocolVersion в _meta тела. Иначе сервер ДОЛЖЕН отклонить запрос статусом 400 Bad Request и ошибкой HeaderMismatch.

Если сервер не реализует запрошенную версию, он ДОЛЖЕН ответить 400 Bad Request и UnsupportedProtocolVersionError со списком поддерживаемых версий. Если не реализует запрошенный метод, ДОЛЖЕН ответить 404 Not Found и ошибкой JSON-RPC с кодом -32601. По тексту ошибки клиент отличает такой ответ от 404 старого сервера HTTP+SSE.

Сервер, который поддерживает клиентов версий до 2025-06-18 (в них заголовка не было), МОЖЕТ считать запрос без заголовка запросом версии 2025-03-26.

Стандартные заголовки запроса

Заголовок Источник Обязателен для
Mcp-Method method всех запросов
Mcp-Name params.name или params.uri tools/call, resources/read, prompts/get

Эти заголовки ОБЯЗАТЕЛЬНЫ. Пример tools/call:

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

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": { "location": "Seattle, WA" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "ExampleClient", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

Заголовки из параметров инструмента

Сервер МОЖЕТ пометить параметры инструмента свойством x-mcp-header в inputSchema. Значение определяет имя заголовка Mcp-Param-{имя}. Серверу это необязательно, а клиенты ДОЛЖНЫ это поддерживать: если определение инструмента содержит такие пометки, клиент ДОЛЖЕН продублировать значения параметров в заголовки.

Ограничения на значения x-mcp-header:

  • не пустое и совпадает с синтаксисом имени поля HTTP (1*tchar, RFC 9110, раздел 5.1);
  • без управляющих символов, включая CR и LF;
  • уникально без учёта регистра в пределах inputSchema;
  • только для параметров примитивных типов: целое, строка, логическое значение (тип number не допускается); целые ДОЛЖНЫ лежать в безопасном диапазоне JavaScript (от −2⁵³+1 до 2⁵³−1);
  • только для свойств, статически достижимых от корня схемы через цепочку ключей properties, без items, oneOf, anyOf, allOf, not, if/then/else и $ref.

Клиенты на Streamable HTTP ДОЛЖНЫ отклонять определения инструментов, где значение нарушает эти ограничения: такой инструмент исключается из результата tools/list, и клиенту СЛЕДУЕТ записать предупреждение. Клиенты на других транспортах (например, stdio) МОГУТ игнорировать x-mcp-header.

Пример определения и итогового запроса:

json
{
  "name": "execute_sql",
  "description": "Execute SQL on Google Cloud Spanner",
  "inputSchema": {
    "type": "object",
    "properties": {
      "region": { "type": "string", "description": "The region to execute the query in", "x-mcp-header": "Region" },
      "query": { "type": "string", "description": "The SQL query to execute" }
    },
    "required": ["region", "query"]
  }
}
http
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: execute_sql
Mcp-Param-Region: us-west1

Кодирование значений

Клиенты ДОЛЖНЫ кодировать значения перед включением в заголовки. Значение приводится к строке: строка как есть, целое в десятичной записи, логическое в виде "true" или "false". Если значение нельзя безопасно передать обычным ASCII (содержит не-ASCII, управляющие символы или пробелы по краям), клиент ДОЛЖЕН использовать Base64 от UTF-8 в формате:

text
Mcp-Param-{Name}: =?base64?{Base64EncodedValue}?=

Так же кодируется Mcp-Name. Метки =?base64? и ?= чувствительны к регистру. Чтобы избежать неоднозначности, клиент ДОЛЖЕН закодировать и обычное значение, похожее на этот шаблон.

Исходное значение Причина Заголовок
us-west1 Обычный ASCII Mcp-Param-Region: us-west1
Hello, 世界 Не-ASCII Mcp-Param-Greeting: =?base64?SGVsbG8sIOS4lueVjA==?=
padded Пробелы по краям Mcp-Param-Text: =?base64?IHBhZGRlZCA=?=

Если значение параметра равно null или параметра нет в аргументах, клиент НЕ ДОЛЖЕН добавлять заголовок.

Проверка на сервере

Сервер, который обрабатывает тело запроса, ДОЛЖЕН отклонять запросы, где значения в заголовках не совпадают со значениями в теле. Это защищает от ситуации, когда балансировщик маршрутизирует по заголовку, а сервер исполняет по телу. Отказ оформляется статусом 400 Bad Request и ошибкой JSON-RPC -32020 (HeaderMismatch):

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32020,
    "message": "Header mismatch: Mcp-Name header value 'foo' does not match body value 'bar'"
  }
}

Причины отказа: нет обязательного заголовка; значение не совпадает с телом (закодированные значения сначала декодируют); недопустимые символы. Целые сравниваются как числа, а не как строки. Имена заголовков регистронезависимы, значения регистрозависимы. Посредникам, которые применяют политику по заголовкам, СЛЕДУЕТ убедиться, что версия протокола требует такой проверки, и отклонять запросы без заголовка.

Обратная совместимость#

Клиент, который поддерживает и современные версии, и прежние (с рукопожатием initialize), МОЖЕТ определить эпоху сервера, попробовав сначала современный запрос. При 400 Bad Request ему СЛЕДУЕТ посмотреть тело: распознанная современная ошибка JSON-RPC значит современный сервер, и нужно повторить запрос с другой версией, а не откатываться. Если тело пустое или ошибка не распознана, нужно вернуться к initialize.

Сервер, который поддерживает только эту ревизию и получает трафик старого клиента, СЛЕДУЕТ действовать так:

  • GET и DELETE на конечную точку: отвечать 405 Method Not Allowed;
  • заголовок Mcp-Session-Id: игнорировать, не создавать и не возвращать идентификаторы сессий;
  • заголовок Last-Event-ID: игнорировать, потоки не возобновляются.

HTTP+SSE (2024-11-05) устарел с версии 2025-03-26 и признан Deprecated по политике жизненного цикла функций. Новым реализациям его применять НЕ СЛЕДУЕТ, существующим СЛЕДУЕТ перейти на Streamable HTTP. Клиент, который хочет поддерживать старые серверы, сначала отправляет POST, а при 400, 404 или 405 без современной ошибки в теле пробует GET и ожидает событие endpoint: так он узнаёт старый транспорт.