# Транспорты

stdio и Streamable HTTP

> Страница сайта MCP Docs — неофициального перевода документации Model Context Protocol (https://mcpdocs.ru/protocol/transports/). Тип: Перевод и пересказ. Основа: Спецификация, раздел «Streamable HTTP» (https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http). Ревизия спецификации 2026-07-28, сверено 05.10.2026.

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

Готовность своего сервера к требованиям этой страницы можно проверить бесплатной [проверкой MCP-сервера](https://mcpprotocol.ru/mcp-check/): она сверяет заголовки, коды ошибок и поведение при чужом `Origin`.

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

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

На stdio клиентам НЕ СЛЕДУЕТ считать отдельную задачу, поток или разговор границей времени жизни процесса. Уведомление `notifications/cancelled` в базовом протоколе используется только на stdio. Проба совместимости для stdio описана на странице [«Версии и совместимость»](https://mcpdocs.ru/protocol/versions/).

> **Заметка переводчика.** Страницу о stdio в спецификации я не переводил: здесь сказано только то, что подтверждается страницами об архитектуре, базовом протоколе и Streamable HTTP. Подробнее смотрите [оригинал](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/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](https://en.wikipedia.org/wiki/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. Клиент ДОЛЖЕН включить [заголовки метаданных запроса](#zagolovki-metadannyh-zaprosa) в каждый 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`: так он узнаёт старый транспорт.
