Транспорт (transport) в Model Context Protocol (MCP) - привязка к каналу связи, которая определяет, как сообщения JSON-RPC между клиентом и сервером разбиваются на части и доставляются, как передаются метаданные запроса и как сообщается об отмене и завершении работы. Смысл сообщений транспорт не меняет: одни и те же запросы и результаты идут по любому транспорту. Спецификация определяет два стандартных транспорта: stdio для локальных серверов и Streamable HTTP для удалённых.
Первая версия спецификации, 2024-11-05, определяла stdio и HTTP с Server-Sent Events (HTTP+SSE). В версии 2025-03-26 HTTP+SSE заменил Streamable HTTP. Текущая версия спецификации - 2026-07-28: она убрала из Streamable HTTP сессии протокола, и с тех пор любой запрос может обработать любой экземпляр сервера. Протокол развивается в Agentic AI Foundation, входящем в Linux Foundation. Общая архитектура MCP описана в статье Что такое MCP.
Кратко
| Параметр | Значение |
|---|---|
| Уровень | Транспортный уровень MCP |
| Формат сообщений | JSON-RPC 2.0, UTF-8 |
| Стандартные транспорты | stdio, Streamable HTTP |
| Разбиение потока в stdio | Одно сообщение JSON-RPC на строку |
| Эндпоинт Streamable HTTP | Один MCP-эндпоинт, принимает POST |
| Ответ сервера по HTTP | Объект JSON или поток SSE в рамках запроса |
| Сессии протокола | Нет с версии 2026-07-28 |
| Устаревший транспорт | HTTP+SSE из 2024-11-05, устарел с 2025-03-26 |
| Текущая спецификация | 2026-07-28 |
Какую задачу решают транспорты
MCP-серверы работают двумя разными способами. Локальный сервер - программа на машине пользователя, которую часто запускает сам хост. Удалённый сервер - сетевой сервис, к которому многие пользователи обращаются через интернет. Этим случаям нужна разная доставка: в одном - канал между двумя процессами, в другом - HTTP с балансировщиками нагрузки, шлюзами и авторизацией.
Протокол отделяет смысл сообщений от их доставки. Уровень данных определяет сообщения JSON-RPC: обнаружение возможностей, примитивы, уведомления, ошибки. Транспортный уровень определяет, как эти сообщения доходят до другой стороны. Поэтому один раз написанный сервер можно развернуть и локально, и удалённо, а его логика не зависит от транспорта.
История
В таблице - изменения транспортов от версии к версии спецификации.
| Версия | Изменения в транспортах |
|---|---|
| 2024-11-05 | stdio и HTTP+SSE: поток SSE для сообщений сервера и отдельный эндпоинт для запросов POST; обязательная проверка Origin |
| 2025-03-26 | Streamable HTTP заменяет HTTP+SSE: один MCP-эндпоинт для POST и GET, необязательный SSE, сессии с Mcp-Session-Id, возобновляемые потоки с Last-Event-ID; пакетная отправка JSON-RPC |
| 2025-06-18 | Пакетная отправка убрана; заголовок MCP-Protocol-Version обязателен в HTTP-запросах |
| 2025-11-25 | HTTP 403 при неверном Origin; сервер может закрыть соединение SSE, а клиент переподключается с Last-Event-ID; stderr разрешён для журнала любого уровня |
| 2026-07-28 | Нет сессий, потока GET и возобновления потоков; заголовки Mcp-Method и Mcp-Name; нет запросов от сервера; subscriptions/listen для уведомлений; HTTP+SSE получил статус Deprecated |
Что определяет транспорт
Все сообщения MCP - сообщения JSON-RPC в кодировке UTF-8. Транспорт доставляет запросы и уведомления от клиента к серверу и ответы и уведомления от сервера к клиенту. В версии 2026-07-28 сервер не отправляет запросы JSON-RPC: когда ему нужны данные от клиента, он возвращает InputRequiredResult в рамках механизма Multi Round-Trip Requests (MRTR), многоэтапных запросов.
Все метаданные протокола передаются в теле сообщения. Каждый запрос несёт версию протокола и возможности (capabilities) клиента в поле _meta. Транспорт может копировать отдельные поля тела в свою оболочку, как Streamable HTTP копирует их в заголовки. Источником истины остаётся тело, а транспорт, который копирует метаданные, определяет, как отклонять расхождения.
Каждый транспорт также определяет, как клиент отказывается от запроса, который ещё выполняется: в stdio клиент отправляет уведомление, в Streamable HTTP закрывает поток ответа на этот запрос. Модель запросов без состояния версии 2026-07-28 описана в статье MCP без состояния.
stdio
Запуск сервера
В транспорте stdio клиент запускает MCP-сервер как дочерний процесс и обменивается с ним данными через стандартные потоки этого процесса. Сервер читает сообщения из стандартного ввода (stdin) и пишет сообщения в стандартный вывод (stdout). Команду запуска хост берёт из своей конфигурации, например из файла claude_desktop_config.json приложения Claude Desktop, который приведён в документации MCP:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/Desktop"
]
}
}
}Процесс сервера работает с теми же правами, что и хост. Спецификация указывает, что серверу на stdio не следует использовать механизм авторизации MCP: учётные данные он берёт из окружения, например из переменных окружения.
Разбиение потока на сообщения
Сообщения разделяются переводом строки и не должны содержать переводов строки внутри, поэтому каждая строка потока - ровно одно сообщение JSON-RPC. Запрос server/discover, записанный в stdin, выглядит так:
{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}Слоя заголовков нет: метод, аргументы и все метаданные находятся в теле JSON. Все сообщения сервера идут по одному каналу, stdout: ответы, которые сопоставляются с запросами по id JSON-RPC, уведомления о выполняющемся запросе и уведомления подписок subscriptions/listen. Подписки клиент различает по полю io.modelcontextprotocol/subscriptionId в _meta.
Стандартный вывод и стандартный поток ошибок
Сервер не должен писать в stdout ничего, кроме корректных сообщений MCP, а клиент не должен писать ничего другого в stdin сервера. Одна отладочная строка в stdout ломает поток. Поэтому руководства по созданию серверов в документации MCP предостерегают от print() в Python и console.log() в JavaScript в серверах на stdio: обе функции по умолчанию пишут в стандартный вывод.
Журнал пишется в стандартный поток ошибок (stderr). Сервер может писать туда любой текст в UTF-8: информационные, отладочные сообщения и сообщения об ошибках. Клиент может перехватывать этот поток, пересылать его или игнорировать и не должен считать вывод в stderr признаком ошибки. С версии 2026-07-28 функция протокола Logging устарела, и спецификация называет stderr её заменой для серверов на stdio.
Отмена запроса
Чтобы отменить выполняющийся запрос, клиент отправляет уведомление notifications/cancelled с идентификатором этого запроса:
{
"jsonrpc": "2.0",
"method": "notifications/cancelled",
"params": { "requestId": 3, "reason": "User cancelled the operation" }
}Серверу следует как можно скорее прекратить работу над запросом, и он не должен отправлять по нему новых сообщений. В stdio сервер тоже использует это уведомление, но только в одном случае: чтобы завершить открытый ранее поток subscriptions/listen.
Завершение и перезапуск
Клиенту следует завершать работу сервера в три шага:
- Закрыть входной поток дочернего процесса.
- Дождаться завершения сервера.
- Если сервер не завершился за разумное время, принудительно завершить процесс. В POSIX-системах это обычно
SIGTERM, затемSIGKILL; в Windows -TerminateProcessили Job Objects.
Серверу следует сразу завершаться, когда закрыт его стандартный ввод: спецификация называет это единственным переносимым сигналом штатного завершения. Если процесс сервера неожиданно завершился, клиенту следует перезапустить его. Запросы, которые выполнялись, теряются, и клиент может отправить их заново новому процессу; активные потоки subscriptions/listen открываются заново.
Доверие между процессами
Согласно документации MCP о безопасности локальных серверов, клиент и сервер на stdio находятся в одной зоне доверия: сервер уже выполняет код на машине пользователя, и транспорт не является песочницей. Изоляцию при необходимости обеспечивает хост или тот, кто управляет машиной, например с помощью контейнера.
Streamable HTTP
Эндпоинт и запросы
В транспорте Streamable HTTP сервер работает как самостоятельный процесс и обслуживает соединения от многих клиентов. Он предоставляет один HTTP-эндпоинт, MCP-эндпоинт, который принимает запросы POST, например https://example.com/mcp.
Каждое сообщение JSON-RPC от клиента - новый HTTP-запрос POST к MCP-эндпоинту. Тело содержит ровно один запрос или одно уведомление. Клиент обязан передать заголовок Accept, в котором перечислены и application/json, и text/event-stream, и заголовки метаданных запроса, описанные ниже. Запрос промпта выглядит так:
POST /mcp HTTP/1.1
Host: example.com
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: prompts/get
Mcp-Name: code_review
{
"jsonrpc": "2.0",
"id": 7,
"method": "prompts/get",
"params": {
"name": "code_review",
"arguments": { "code": "def hello():\n print('world')" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "example-client", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}Если тело - уведомление и сервер его принял, сервер отвечает 202 Accepted без тела. Ядро протокола версии 2026-07-28 не определяет уведомлений клиента для Streamable HTTP, так что это правило описывает только механику транспорта.
Ответы
Server-Sent Events (SSE) - формат HTTP-ответа, в котором сервер передаёт поток событий в рамках одного ответа. На запрос сервер отвечает в одной из двух форм, и клиент обязан поддерживать обе:
Content-Type: application/json: один объект JSON с ответом JSON-RPC.Content-Type: text/event-stream: поток SSE в рамках этого запроса. Поток может передавать уведомления, связанные с запросом, напримерnotifications/progress, а затем итоговый ответ, которым поток следует завершить.
В отличие от версий до 2025-11-25 включительно, сервер не должен отправлять в потоке собственные запросы JSON-RPC. Открывая поток SSE, серверу следует передать заголовок X-Accel-Buffering: no, чтобы обратные прокси, например nginx, не буферизовали события.
Потоки уведомлений
Отдельный поток GET прежних версий в 2026-07-28 убран. Чтобы получать уведомления об изменениях, например notifications/tools/list_changed, клиент отправляет запрос subscriptions/listen методом POST. Ответ на него - поток SSE, который остаётся открытым и передаёт только запрошенные клиентом типы уведомлений. Уведомления о прогрессе и другие уведомления отдельного запроса приходят только в поток ответа на этот запрос.
Заголовки запроса
Streamable HTTP копирует отдельные поля тела JSON-RPC в HTTP-заголовки. Балансировщики нагрузки, шлюзы и средства наблюдения могут тогда маршрутизировать и анализировать запросы, не разбирая тело. В таблице - заголовки версии 2026-07-28.
| Заголовок | Источник в теле | Обязателен для |
|---|---|---|
MCP-Protocol-Version |
io.modelcontextprotocol/protocolVersion в _meta |
Каждого запроса POST |
Mcp-Method |
method |
Всех запросов |
Mcp-Name |
params.name или params.uri |
tools/call, resources/read, prompts/get |
Mcp-Param-{Name} |
Параметр инструмента, помеченный x-mcp-header |
Вызовов инструментов, которые объявляют такие параметры |
Заголовки Mcp-Param-{Name} создаются из свойства x-mcp-header во входной схеме инструмента, см. статью Инструменты (tools) в MCP. Серверы могут использовать это свойство, клиенты обязаны его поддерживать.
Значение Mcp-Name или Mcp-Param-{Name}, которое нельзя передать как обычный заголовок ASCII, кодируется в Base64 в виде =?base64?{value}?=. Имена заголовков сравниваются без учёта регистра, а значения, например имена методов, чувствительны к регистру.
Проверка заголовков
Сервер, который обрабатывает тело, обязан отклонить запрос, если заголовок не совпадает с телом, если нет обязательного заголовка или если в заголовке недопустимые символы. Перед сравнением сервер декодирует значения в форме Base64. Отказ - 400 Bad Request с ошибкой JSON-RPC -32020 (HeaderMismatch):
{
"jsonrpc": "2.0",
"id": 7,
"error": {
"code": -32020,
"message": "Header mismatch: Mcp-Method header value 'tools/call' does not match body value 'prompts/get'"
}
}Проверка не даёт сетевым компонентам опираться на разные источники истины, например когда балансировщик маршрутизирует по заголовку, а сервер выполняет тело.
Коды состояния HTTP
В таблице - коды состояния, которые спецификация определяет для транспорта.
| Код | Когда |
|---|---|
200 OK |
Ответ на запрос: объект JSON или поток SSE |
202 Accepted |
Уведомление принято; тела нет |
400 Bad Request |
Расхождение заголовков (-32020), нет обязательной возможности клиента (-32021), неподдерживаемая версия протокола (-32022), уведомление, которое нельзя принять |
403 Forbidden |
Неверный заголовок Origin |
404 Not Found |
Сервер не реализует метод; ошибка JSON-RPC -32601 |
405 Method Not Allowed |
GET или DELETE от клиента прежней версии |
Авторизация добавляет 401 Unauthorized и 403 Forbidden для отсутствующих, неверных или недостаточных токенов.
Отмена и обрыв потока
В Streamable HTTP закрытие потока SSE с ответом означает отмену этого запроса. У каждого запроса свой поток, поэтому разрыв однозначен, и уведомление notifications/cancelled не ожидается. Серверу следует прекратить работу над запросом, и он не должен отправлять по нему новых сообщений.
С версии 2026-07-28 потоки нельзя возобновить: заголовок Last-Event-ID и идентификаторы событий SSE убраны. Если поток ответа оборвался, выполнявшийся запрос теряется. Клиент обязан отправить его заново как новый запрос с новым идентификатором.
Отсутствие сессий и балансировка нагрузки
Версия 2026-07-28 убрала сессии протокола и заголовок Mcp-Session-Id. Согласно анонсу выпуска, теперь любой запрос может попасть на любой экземпляр сервера за обычным балансировщиком нагрузки с распределением по кругу (round-robin), без общего хранилища.
Состояние, которое должно сохраняться между вызовами, теперь передаётся явно: инструмент возвращает идентификатор, а модель передаёт его аргументом следующего вызова. Изменения выпуска и миграция описаны в статье MCP без состояния.
Безопасность локальных HTTP-серверов
Спецификация транспорта содержит три требования безопасности:
- Серверы обязаны проверять заголовок
Originвсех входящих соединений, чтобы предотвратить атаки DNS rebinding. ЕслиOriginпередан и неверен, сервер обязан ответить403 Forbidden. - Серверу, который работает локально, следует слушать только localhost (127.0.0.1), а не все сетевые интерфейсы (0.0.0.0).
- Серверам следует реализовать аутентификацию для всех соединений.
Без этих мер, согласно спецификации, злоумышленники могут с помощью DNS rebinding обращаться к локальным MCP-серверам с удалённых веб-сайтов. Документация MCP добавляет, что порт на 127.0.0.1 доступен другим локальным процессам и, через браузер, веб-сайтам.
Привязка к 127.0.0.1 не является границей аутентификации. Локальному HTTP-серверу нужны также проверка
Originи аутентификация.
Другие риски описаны в статье Безопасность MCP.
Авторизация
Авторизация в MCP необязательна и относится к транспортам на основе HTTP. Реализациям на HTTP следует соблюдать механизм авторизации из спецификации, основанный на OAuth 2.1: клиент передаёт токен в заголовке Authorization каждого запроса. Серверам на stdio не следует его использовать: они берут учётные данные из окружения. Подробности - в статье Авторизация в MCP.
Обратная совместимость
Прежние редакции Streamable HTTP
Версии с 2025-03-26 по 2025-11-25 использовали Streamable HTTP с сессиями (Mcp-Session-Id, завершение запросом DELETE), отдельным потоком GET, запросами сервера в потоках SSE и возобновлением через Last-Event-ID. Сервер, который поддерживает только 2026-07-28, отвечает на GET и DELETE кодом 405 Method Not Allowed и игнорирует Mcp-Session-Id и Last-Event-ID.
Клиент, который поддерживает и версии с рукопожатием initialize, сначала отправляет запрос в текущем формате. При ответе 400 Bad Request он смотрит на тело: известная ошибка JSON-RPC текущей версии означает современный сервер, а пустое тело или неизвестная ошибка - сервер прежней версии, и тогда клиент переходит на initialize. В stdio клиент проверяет сервер запросом server/discover. Переход не должен зависеть от одного конкретного кода ошибки, потому что прежние серверы отвечают на неизвестные запросы по-разному или не отвечают вовсе.
Транспорт HTTP+SSE
Транспорт HTTP+SSE версии 2024-11-05 использовал два эндпоинта. Клиент открывал поток SSE, сервер отправлял событие endpoint с адресом для сообщений, и клиент отправлял все сообщения запросами POST на этот адрес.
HTTP+SSE устарел с версии 2025-03-26. Версия 2026-07-28 присвоила ему статус Deprecated по политике жизненного цикла функций (SEP-2596): новым реализациям не следует его использовать. Согласно реестру устаревших функций, его можно удалить не раньше чем через три месяца после того, как SEP-2596 получил статус Final.
Сервер может сохранить старые эндпоинты рядом с новым MCP-эндпоинтом. Клиент сначала отправляет запрос POST на адрес сервера. Если ответ - 400, 404 или 405 без известной ошибки JSON-RPC текущей версии, он отправляет запрос GET, и событие endpoint первым в потоке означает HTTP+SSE.
Собственные транспорты
Клиенты и серверы могут реализовать собственные транспорты. Протокол не зависит от транспорта и может работать поверх любого канала с двусторонним обменом сообщениями. Собственный транспорт обязан сохранять формат сообщений JSON-RPC, шаблоны обмена сообщениями и модель метаданных в каждом запросе. Ему следует документировать, как устанавливается соединение, как разбивается поток на сообщения и как работает отмена.
Собственному транспорту поверх надёжного двустороннего потока байтов, например сокетов Unix или TCP, следует использовать разбиение потока из stdio: одно сообщение JSON-RPC на строку.
Сравнение stdio и Streamable HTTP
В таблице сравниваются два стандартных транспорта.
| stdio | Streamable HTTP | |
|---|---|---|
| Типичное развёртывание | Локальный сервер на машине пользователя | Удалённый сервер, сетевой сервис |
| Кто запускает сервер | Клиент, как дочерний процесс | Оператор; сервер работает самостоятельно |
| Клиентов у одного процесса сервера | Один, тот, кто его запустил | Много |
| Разбиение на сообщения | Одно сообщение JSON-RPC на строку | Одно сообщение на запрос POST |
| Метаданные запроса | Только в теле | В теле и в заголовках |
| Сообщения сервера | Один общий канал stdout |
Объект JSON или поток SSE на каждый запрос |
| Уведомления об изменениях | В stdout, с идентификатором подписки |
В потоке SSE запроса subscriptions/listen |
| Отмена | notifications/cancelled |
Закрытие потока ответа |
| Журнал | stderr |
На стороне сервера, например OpenTelemetry |
| Учётные данные | Из окружения процесса | Механизм авторизации на основе OAuth 2.1 |
| Главный вопрос безопасности | Общая зона доверия с хостом | Проверка Origin, аутентификация |
Выбор транспорта следует из того, где должен работать сервер. Согласно документации MCP, чтение локальных файлов, управление локальным инструментом или доступ к оборудованию - причины запускать сервер локально, а серверу, который только оборачивает удалённый API, это ничего не даёт. Удалённый сервер на Streamable HTTP не выполняет код на машине пользователя и использует механизм авторизации вместо токена в локальном файле конфигурации.
Логика сервера не зависит от транспорта, поэтому один сервер может поддерживать оба: тогда одни и те же инструменты, ресурсы и промпты доступны и локально, и удалённо.
Термины
| Термин | Значение |
|---|---|
| Транспорт (transport) | Привязка к каналу связи, которая определяет, как сообщения MCP разбиваются и доставляются |
| stdio | Транспорт через стандартные потоки дочернего процесса, который запускает клиент |
| Streamable HTTP | Транспорт, в котором каждое сообщение - HTTP-запрос POST к одному MCP-эндпоинту |
| MCP-эндпоинт | Единственный HTTP-путь сервера, который принимает запросы POST с сообщениями MCP |
| Server-Sent Events (SSE) | Формат HTTP-ответа с потоком событий от сервера |
| HTTP+SSE | Транспорт версии 2024-11-05 с двумя эндпоинтами; устарел с 2025-03-26 |
| DNS rebinding | Атака, при которой веб-сайт через браузер обращается к серверу на машине пользователя |
| Собственный транспорт | Транспорт вне спецификации, который сохраняет её сообщения и метаданные |
Источники
- Transports overview, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- stdio, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Streamable HTTP, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Subscriptions, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Обзор базового протокола, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Ключевые изменения 2026-07-28(откроется в новой вкладке) (на английском)
- Устаревшие функции, спецификация 2026-07-28(откроется в новой вкладке) (на английском)
- Transports, спецификация 2025-11-25(откроется в новой вкладке) (на английском)
- Transports, спецификация 2025-03-26(откроется в новой вкладке) (на английском)
- Transports, спецификация 2024-11-05(откроется в новой вкладке) (на английском)
- Local Server Security, документация MCP(откроется в новой вкладке) (на английском)
- Build an MCP server, документация MCP(откроется в новой вкладке) (на английском)
- The 2026-07-28 Specification, блог MCP, 28 июля 2026(откроется в новой вкладке) (на английском)