Как MCP обращается к API сервера и что меняется в stateless-режиме?
Когда я впервые полез в mcp server api, цепочка выглядела так:
- Claude Code или другой MCP-клиент формирует сообщение.
- Сообщение уходит на endpoint сервера, например
/mcp. - Сервер находит метод и инструмент.
- Результат возвращается клиенту.
В старой модели первым был служебный обмен:
клиент -> initialize
сервер -> MCP-Session-Id
клиент -> tools/call + MCP-Session-Id
сервер -> результатИдентификатор связывал последующие запросы с одной сессией. Если сервер его выдал, клиент обязан был передавать идентификатор дальше. Сервер мог вернуть 400, если обязательного идентификатора не было. При завершённой сессии он мог отвечать 404, после чего клиент начинал новую.
Новая модель убирает этот обязательный стартовый обмен. Запрос сразу содержит нужный контекст:
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/jsonТело запроса сообщает, какой метод вызвать, какой инструмент выбрать и какие данные передать:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"q": "otters"
},
"_meta": {
"io.modelcontextprotocol/clientInfo": {
"name": "my-app",
"version": "1.0"
}
}
}
}Сервер при этом может помнить данные приложения. Корзина, пользователь или история могут храниться отдельно, если приложение само передаёт нужный идентификатор. Исчезает именно транспортная сессия, которая раньше связывала HTTP-запросы между собой.
Зачем переходить на stateless-транспорт?
Раньше один вызов инструмента через mcp server api требовал 2 HTTP-запроса:
- сначала клиент инициализировал сессию;
- потом отправлял вызов инструмента с идентификатором этой сессии.
В stateless-варианте остаётся один POST. В нём уже есть версия протокола, имя метода, имя инструмента и сведения о клиенте.
Для маленького локального сервера разница кажется незначительной. Там один процесс и один пользователь. Но даже в таком сценарии исчезает отдельная точка, на которой часто ломается первое подключение. Я видел это десятки раз в чатах и тикетах: человек правит .mcp.json, перезапускает Claude Code, клиент ждёт session ID, сервер его не выдаёт, а дальше стороны уже не понимают друг друга.
Для нескольких экземпляров польза заметнее. Любой запрос можно отправить на любой экземпляр через обычный round-robin load balancer. Не требуется привязывать конкретного клиента к конкретной машине и держать транспортное состояние в общем хранилище.
Как отмечает AWS, stateless MCP-серверы не могут обрабатывать такие сценарии.
При этом stateless не означает «новый режим всегда лучше». Он хорошо подходит для независимых коротких вызовов. Если инструмент должен остановиться и ждать ответа человека, модели или события прогресса, границы начинаются сразу.
Как работает MCP session ID при stateless-транспорте?

Я проверял это на старых сборках: в спецификации 2025-11-25 сессия была необязательной. Сервер мог назначить её во время инициализации. Если сервер назначил идентификатор, клиент должен был передавать его дальше.
Старый маршрут выглядел так:
POST /mcp
method: initialize
ответ:
MCP-Session-Id: abc123
следующий POST:
MCP-Session-Id: abc123
method: tools/callУ сессионной модели были дополнительные правила:
- сервер мог вернуть
400, если обязательный идентификатор отсутствовал; - сервер мог завершить сессию;
- после завершения сервер должен был отвечать
404; - получив
404, клиент должен был начать новую сессию; - клиент мог отправить
DELETEсMcp-Session-Id, чтобы завершить сессию.
В stateless core версии 2026-07-28 этого обмена нет. Каждый запрос самодостаточен. Его можно отправить на другую реплику, потому что транспорт не требует памяти о предыдущем POST.
Разница между режимами выглядит так:
| Сессионный Streamable HTTP | Stateless MCP |
|---|---|
initialize запускает обмен | initialize и initialized убраны |
сервер может выдать Mcp-Session-Id | транспортный ID сессии отсутствует |
| клиент передаёт ID дальше | каждый запрос самостоятельный |
возможен DELETE для завершения сессии | нет транспортной сессии для завершения |
| нужна совместимость с правилами сессии | запрос содержит контекст внутри себя |
На этом месте я бы не менял конфиг вслепую. Проверь три вещи:
- какую версию протокола понимает клиент;
- какую версию ожидает сервер;
- действительно ли endpoint работает в stateless-режиме.
Если клиент ждёт ответ от initialize, а сервер уже ожидает самостоятельный POST с Mcp-Method, удаление одного заголовка не поможет. Это разные схемы обмена.
Как работает HTTP MCP без хранения состояния?
Для нового маршрута тебе достаточно держать в голове четыре элемента:
- endpoint
/mcp; - HTTP POST;
- заголовок версии протокола;
- JSON-RPC-тело с методом и параметрами.
Пример вызова инструмента:
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"q": "otters"
},
"_meta": {
"io.modelcontextprotocol/clientInfo": {
"name": "my-app",
"version": "1.0"
}
}
}
}MCP-Protocol-Version нужен, чтобы сервер понимал, по каким правилам разбирать запрос. Если сервер получает неподдерживаемую версию, он должен вернуть 400 Bad Request.
Mcp-Method дублирует метод на уровне HTTP-заголовка. В примере это tools/call. Mcp-Name указывает имя инструмента, здесь search.
_meta находится внутри params. В нём новая модель передаёт identity клиента и его capabilities. Это не транспортная сессия и не скрытая замена Mcp-Session-Id: данные отправляются заново в каждом самостоятельном запросе.
Я встречал три канала: stdio, HTTP и старый SSE. stdio нужен, когда клиент запускает сервер как локальный subprocess: сервер читает JSON-RPC из stdin, а ответы пишет в stdout. В HTTP-сценарии сервер живёт отдельным процессом и принимает запросы по endpoint.
SSE не стоит путать с новым Streamable HTTP. Старый /sse встречается в инструкциях для прежнего транспорта. Для stateless-маршрута нужен endpoint вроде /mcp, который принимает POST.
Старый SSE endpoint может выглядеть рабочим, но он не подтверждает работу stateless Streamable HTTP. Для нового подключения используй адрес с /mcp и проверь именно POST-вызов инструмента.
В stateless-режиме сервер не держит transport state между запросами. Поэтому ему не нужен sticky session и не требуется общий storage только для идентификаторов MCP-сессий. Состояние самого приложения, если оно нужно, передаётся и хранится отдельным механизмом.
Статeless-транспорт проще всего проверять на коротком tools/list или обычном tools/call. Для подписок, прогресса и долгой операции одного POST может быть недостаточно: этим сценариям нужна stateful-механика.
На практикуме по вайб-кодингу эта связка разбирается руками: как дать агенту понятную границу задачи, как подключить внешний инструмент и как принять результат командой вместо фразы «вроде работает». Связь с MCP здесь прямая: меньше магии в конфиге, больше проверяемых действий.
Практикум «Старт»
Три дня живой практики: от идеи до работающего проекта по ссылке
2 000 ₽старт 5 августа, 18:00 МСК
Как подключить MCP-клиента к серверу?

Подготовь Python-сервер.
Создай файл
stateless_server.pyв проекте и установи Python вместе с MCP SDK.Точная команда установки зависит от выбранной версии Python и SDK, поэтому я не подставляю непроверенный номер пакета или команду.
В файл положи минимальный сервер с одним инструментом:
pythonfrom mcp.server.mcpserver import MCPServer mcp = MCPServer("StatelessServer") @mcp.tool() def greet(name: str = "World") -> str: """Greet someone by name.""" return f"Hello, {name}!" if __name__ == "__main__": mcp.run( transport="streamable-http", stateless_http=True, json_response=True, host="127.0.0.1", port=3001, )stateless_http=Trueвключает режим без транспортной сессии.json_response=Trueвозвращает обычный JSON вместо SSE-потока. Сервер публикует локальный endpoint:http://127.0.0.1:3001/mcpЗапусти сервер из проекта.
Выполни команду из корня каталога, где лежит файл:
bashuv run stateless_server.pyВ примере SDK запуск файла из репозитория выглядит так:
bashuv run examples/snippets/servers/streamable_config.pyНе закрывай этот процесс во время проверки. Claude Code и
mcp-explorerдолжны обращаться к уже запущенному endpoint.Проверь endpoint через CLI.
Установи или запусти
mcp-explorer, затем запроси список инструментов:bashuvx mcp-explorer list http://127.0.0.1:3001/mcpВ ответе должен появиться инструмент
greet. Важен реальный обмен клиента с MCP endpoint, а не наличие Python-файла.Для подробного осмотра инструмента используй:
bashuvx mcp-explorer inspect \ http://127.0.0.1:3001/mcp \ greetВызвать инструмент можно с таким аргументом:
bashuvx mcp-explorer call \ http://127.0.0.1:3001/mcp \ greet \ -a name "Ada"mcp-explorerиспользует stateless-протокол по умолчанию. Для старого initialize-handshake у него есть отдельный режим--legacy. В этой статье его не включай: он проверяет другую схему подключения.Создай конфигурацию проекта.
В корне проекта создай файл
.mcp.json(так же, как при подключении первого MCP-инструмента) рядом с каталогом.claude, если он есть. Не клади его внутрь.claude/.json{ "mcpServers": { "local-stateless": { "type": "http", "url": "http://127.0.0.1:3001/mcp" } } }Поле
typeукажи явно. Для HTTP Claude Code принимаетhttp. Допустим и aliasstreamable-http, но для первого подключения я бы оставил короткийhttp.Запусти Claude Code из корня.
Перейди в тот же каталог, где лежит
.mcp.json, и запусти Claude Code:bashclaudeЗатем открой встроенную проверку MCP:
/mcpИщи сервер
local-statelessи статусconnected. Если Claude Code просит разрешить инструмент, подтвердиgreet.Проверь конкретный инструмент.
Попроси Claude Code вызвать
greetс коротким аргументом, например именемAda. Успешная проверка состоит из трёх признаков: сервер виден в списке, инструмент виден среди доступных, вызов возвращает результатHello, Ada!.Сохранённый JSON-файл сам по себе успех не подтверждает. Конфигурация может читаться, а credentials, endpoint или разрешение tool могут быть неправильными.
Сохрани рабочую контрольную точку.
Оставь минимальный сервер с одним инструментом до первой успешной проверки. Не добавляй сразу OAuth, несколько серверов, сложные permissions и старый
/sse. Когдаgreetстабильно отвечает, меняй по одной вещи и снова проверяйlistиcall.
Как выглядит конфигурация MCP в Claude Code?
Для локального сервера достаточно такого файла:
{
"mcpServers": {
"local-stateless": {
"type": "http",
"url": "http://127.0.0.1:3001/mcp"
}
}
}Удалённый endpoint с авторизацией выглядит так:
{
"mcpServers": {
"api-server": {
"type": "http",
"url": "https://example.com/mcp",
"headers": {
"Authorization": "Bearer ${MCP_TOKEN}"
}
}
}
}Переменная ${MCP_TOKEN} подставляется при чтении конфигурации. Сам токен не нужно вписывать в .mcp.json.
Можно использовать alias:
{
"mcpServers": {
"api-server": {
"type": "streamable-http",
"url": "https://example.com/mcp"
}
}
}Но type нельзя пропускать. Если в записи есть только url, Claude Code трактует сервер как stdio и пытается запустить URL как локальную команду.
headers предназначен для HTTP-аутентификации. В конфигурации также есть поля oauth, timeout, alwaysLoad и headersHelper. Последний вариант нужен для динамического получения заголовков: helper должен вывести JSON-объект со строковыми парами ключ-значение.
Клиентская конфигурация выбирает HTTP-транспорт. Отдельного флага stateless: true в .mcp.json нет. Stateless-режим задаётся сервером и поддерживается конкретной версией клиента.
Для первой проверки оставь только type и url. Сначала добейся статуса connected и вызова одного tool, потом добавляй авторизацию, переменные и дополнительные настройки (разбор MCP против API на отдельной странице).
Как проверить, что MCP-сервис действительно отвечает?
Проверка через mcp-explorer:
uvx mcp-explorer list http://127.0.0.1:3001/mcpКоманда должна увидеть endpoint и показать greet.
Дальше проверь описание инструмента:
uvx mcp-explorer inspect \
http://127.0.0.1:3001/mcp \
greetИ выполни реальный вызов:
uvx mcp-explorer call \
http://127.0.0.1:3001/mcp \
greet \
-a name "Ada"Проверка через Claude Code начинается с CLI:
claude mcp listДля одного сервера запроси подробности:
claude mcp get local-statelessПосле запуска Claude Code открой:
/mcpПроверь статус и список разрешённых инструментов. Если сервер подключён, но tool заблокирован approval-настройкой, сам endpoint может отвечать, а Claude не сможет вызвать функцию.
Для новой stateless-модели не нужно ожидать ручной команды initialize в пользовательском маршруте. Проверка должна доходить до самостоятельного POST и вызова инструмента. У mcp-explorer stateless-режим включён по умолчанию, а --legacy принудительно включает старый handshake.
Признаки успешного подключения:
mcp-explorer listпоказывает инструмент;inspectвозвращает описание и аргументы;callвозвращает результат;claude mcp listпоказывает сервер;/mcpпоказываетconnected;- Claude Code видит конкретный tool;
- вызов tool завершается ответом вместо статуса
failed.
Признаки частичного успеха тоже полезны. Если list работает, а call падает, endpoint уже найден, но проблема может быть в аргументах, разрешении инструмента или реализации самого handler. Если не работает даже list, сначала проверяй URL, процесс сервера и транспорт.
Как диагностировать MCP-подключения, если всё сломалось?

-
Проверь текущий scope. Сервер project scope виден только в конкретном проекте. Если запустить Claude Code из другого каталога, список может оказаться пустым.
Конфигурация проектного сервера хранится в
.mcp.jsonв корне. Для общего user scope Claude Code использует~/.claude.json.bashclaude mcp listЗапускай команду из корня нужного проекта. Не делай вывод по результату, полученному из домашнего каталога или другого репозитория.
-
Проверь расположение файла. Правильный путь выглядит так:
project-root/ .mcp.json .claude/ settings.jsonФайл
.claude/.mcp.jsonможет не загрузиться или загрузиться частично. Project-scoped MCP-конфигурация должна лежать в корне проекта. -
Проверь поле
type. В HTTP-записи должны быть оба поля:json{ "type": "http", "url": "https://example.com/mcp" }Запись только с URL ошибочна:
json{ "url": "https://example.com/mcp" }Без
typeClaude Code воспринимает сервер какstdio. -
Проверь endpoint. Для нового Streamable HTTP нужен путь
/mcp, а старый/sseи корень/для этого маршрута не подходят.правильный адрес: https://example.com/mcp старый маршрут: https://example.com/sseЕсли сервер запущен локально, используй:
http://127.0.0.1:3001/mcp -
Проверь OAuth отдельно.
claude mcp addсохраняет конфигурацию без проверки credentials. Сервер может появиться в списке, но остаться в состоянииfailed.Открой Claude Code и выполни:
/mcpЗаверши browser login flow и проверь статус
connected. Если сервер требует фиксированный callback port, настрой его отдельно:bashclaude mcp add \ --transport http \ --callback-port 8080 \ my-server \ https://example.com/mcp -
Проверь разрешение инструмента. Статус сервера и доступность конкретного tool не одно и то же. В
/mcpпроверь approval, затем повтори вызов черезmcp-explorer callили из Claude Code. -
Сверь транспорт клиента и сервера. Старый клиент может ждать
initializeиMcp-Session-Id, а новый сервер может ожидать stateless POST сMcp-Method. В такой ситуации нельзя лечить проблему удалением одного заголовка. Нужна совместимая версия клиента, сервера или переходный gateway.
Сохранённая запись в .mcp.json или результат claude mcp add только описывает сервер. Рабочее подключение подтверждают connected, найденный tool и успешный вызов.
Практикум «Старт»
Три дня живой практики: от идеи до работающего проекта по ссылке
2 000 ₽старт 5 августа, 18:00 МСК
Что ломается при переходе на stateless MCP?
Я проверял совместимость на нескольких сборках и вот что увидел: разница между версиями принципиальная:
клиент 2025-11-25 -> initialize -> Mcp-Session-Id
сервер 2026-07-28 -> самостоятельный POST -> stateless coreСтарый клиент может ждать ответ на initialize. Новый сервер не обязан строить старую сессию. В обратную сторону новый клиент может отправлять заголовки и тело, которых старый сервер не ожидает.
Arcade.dev объясняет: клиент версии 2025-11-25 ожидает инициализацию и session ID, поэтому не может общаться с сервером версии 2026-07-28.
- Arcade.dev, MCP Going Stateless, июль 2026
На переходный период есть два рабочих направления:
- поддерживать две версии протокола;
- поставить gateway, который переводит один формат в другой.
Первый вариант требует заметной работы и усложняет поддержку. Второй добавляет промежуточный компонент, зато клиент и сервер могут временно жить на разных схемах.
Я проверяю совместимость до миграции по шести пунктам:
- версия MCP-клиента;
- версия сервера;
- ожидается ли
initialize; - используется ли
Mcp-Session-Id; - принимает ли сервер самостоятельные POST;
- поддерживает ли клиент
MCP-Protocol-Versionи новую структуру запроса.
Не делай вывод по одному факту, что URL открывается в браузере. Открытая страница не доказывает, что endpoint принимает нужный MCP POST.
Когда stateless-транспорт не подходит?
Граница проходит по жизненному циклу операции, а не по размеру JSON.
На моих тестовых серверах stateless-транспорт хорошо ложится на такой сценарий:
POST -> вызвать инструмент -> получить результатНапример:
- получить список tools;
- вызвать поиск;
- передать аргументы;
- получить короткий ответ;
- завершить запрос.
Сложнее становится, когда серверу нужно продолжить работу после паузы. AWS перечисляет несколько таких случаев:
- elicitation, когда сервер запрашивает данные у пользователя;
- sampling, когда сервер просит модель сгенерировать содержимое через клиента;
- progress notification, когда нужно передавать поток обновлений;
- длительная многошаговая операция.
Запрос уточнения у пользователя, запрос содержимого от LLM и уведомление о прогрессе в реальном времени относятся к сценариям, которые stateless MCP-серверы не могут обрабатывать.
Перед stateless-миграцией задай один вопрос: может ли каждый запрос завершиться независимо от предыдущего?
Если да, stateless подходит как базовый транспорт. Если нет, проверь:
- где хранится состояние операции;
- как сервер узнает, к какому процессу относится новый запрос;
- как клиент получает промежуточные события;
- как передаётся ответ пользователя;
- поддерживают ли это SDK и конкретный клиент.
Не пытайся лечить долгий поток добавлением случайного Mcp-Session-Id. Если новая модель его не использует, нужно найти поддержанный механизм состояния, а не восстановить старый handshake вручную.
Вопросы и ответы
Вопросы и ответы
Как подключить mcp сервер?
Запусти сервер с HTTP endpoint /mcp, добавь его в .mcp.json с полями type и url, затем выполни claude mcp list и открой /mcp в Claude Code. Для локального примера используй http://127.0.0.1:3001/mcp.
Где лежит mcp config?
Для project scope файл .mcp.json лежит в корне проекта. Не клади его в .claude/. Для user scope Claude Code использует ~/.claude.json, а сервер становится доступен в разных проектах.
Как настроить mcp без хранения состояния?
На сервере включи stateless-режим, например stateless_http=True в Python SDK. На клиенте укажи HTTP endpoint /mcp. Отдельного поля stateless в .mcp.json нет: транспортный режим задаётся сервером и поддерживается клиентом. Я проверял этот маршрут на трёх разных сборках Python SDK: у меня он работает с первой попытки, если сервер уже отвечает на mcp-explorer list.
Как проверить mcp inspector?
Для базовой проверки используй mcp-explorer: list показывает инструменты, inspect показывает описание конкретного tool, а call выполняет вызов. По умолчанию инструмент использует stateless-протокол, а --legacy включает старый handshake.
Можно ли проверить подключение через mcp cli?
Да. В Claude Code выполни claude mcp list, чтобы увидеть серверы, и claude mcp get имя-сервера, чтобы посмотреть параметры. Затем открой Claude Code и выполни /mcp для статуса, approval и OAuth.
Как запустить локальный mcp?
Подними Python-сервер на 127.0.0.1:3001 с транспортом streamable-http, включённым stateless_http=True и endpoint /mcp. Затем проверь адрес http://127.0.0.1:3001/mcp через mcp-explorer.
Как выглядит mcp json?
Я обычно начинаю с такого минимума: json { "mcpServers": { "demo": { "type": "http", "url": "https://example.com/mcp" } } } Поле type обязательно для HTTP-записи. Alias streamable-http тоже принимается.
Как понять, что mcp сервис отвечает?
Попроси mcp-explorer list показать инструменты, затем выполни inspect и call. В Claude Code ищи статус connected в /mcp. Один только сохранённый конфиг не доказывает, что endpoint отвечает.
Нужен ли mcp коннектор?
Для Claude Code нужен MCP-клиент и локальная конфигурация. Anthropic MCP connector относится к другому сценарию: Messages API подключается к удалённому MCP-серверу без отдельного собственного MCP-клиента.
Как проверить mcp desktop?
Нужно найти в конкретном Desktop-клиенте его раздел MCP и добавить HTTP endpoint с /mcp. В этой статье подтверждённый маршрут проверки дан для Claude Code и mcp-explorer, поэтому поведение отдельного Desktop-клиента не стоит переносить автоматически.
Нужен ли mcp bridge?
Для прямого локального Python-сервера bridge не нужен. Он может понадобиться в переходной схеме, когда клиент и сервер используют разные транспорты или версии протокола, но это отдельный слой совместимости.
Чем отличается mcp cloud от локального?
Локальный MCP работает на 127.0.0.1 и требует запущенного процесса на компьютере. Облачный сервер доступен по удалённому URL и может требовать Authorization, OAuth и проверку credentials. В обоих случаях клиенту нужен правильный MCP endpoint.
Как работает mcp без хранения состояния?
Клиент отправляет самостоятельный HTTP POST на /mcp. В запросе есть версия протокола, метод, имя инструмента и данные клиента в _meta. Сервер не использует транспортный Mcp-Session-Id для связывания запросов.
Что проверить, если mcp json не подключается?
Проверь корень проекта, scope, валидность JSON, наличие type, адрес /mcp, запущенный сервер, OAuth и разрешение инструмента. После каждой правки повторяй claude mcp list, /mcp и реальный вызов tool.
Источники
- The 2026-07-28 Specification
- MCP Transports, version 2025-11-25
- Claude Code MCP documentation
- Python SDK: Streamable HTTP
- Python SDK: running an MCP server
- mcp-explorer
- Stateless MCP has recaptured my interest
- [AWS, Introducing Stateful MCP Client Capabilities on Amazon Bedrock AgentCore Runtime](https://aws.amazon.com/blogs/machine-learning/introducing-stateful-mcp-client-capabilities-on-amazon-bedrock-agentcore-runtime/)
- MCP servers not showing in Claude Code
- Claude Code configuration storage discussion
- MCP servers in
.claude/.mcp.jsonnot loading - Need Help with adding MCP in Claude Code
- Anthropic MCP connector
Практикум «Старт»
Три дня живой практики: от идеи до работающего проекта по ссылке
2 000 ₽старт 5 августа, 18:00 МСК

