Даніла (Dayfing)
Назад да публікацый
1908 слоў9 хв

MCP 2026-07-28: stateless-серверы і бяспечная міграцыя

Рэдакцыя Model Context Protocol ад 28 ліпеня 2026 года змяняе спосаб працы аддаленага MCP. Ядро пратакола цяпер не мае стану і працуе з асобнымі запытамі. Кожны запыт утрымлівае даныя, патрэбныя для апрацоўкі, таму балансіроўшчык можа накіраваць наступны выклік на іншы асобнік сервера. Рэліз таксама дадае server/discover, шматраундавыя запыты Multi Round-Trip Requests (MRTR), абавязковыя загалоўкі маршрутызацыі, падказкі кэшавання, framework пашырэнняў і больш строгія правілы аўтарызацыі. Нарматыўныя дэталі гэтага матэрыялу ўзяты з афіцыйнага паведамлення пра рэліз і журнала змяненняў спецыфікацыі.

Гэта змяненне стану пратакола, а не патрабаванне зрабіць усю бізнес-сістэму stateless. Інструмент па-ранейшаму можа выкарыстоўваць базу даных, чаргу або доўгажывучы workflow. Знікае схаваны стан, прывязаны да транспартнай сесіі MCP, што важна пры пераходзе ад аднаго працэсу да некалькіх рэплік.

Што змянілася ў параўнанні з пратаколам 2025 года

Раней жыццёвы цыкл пачынаўся запытам initialize, за якім ішло апавяшчэнне notifications/initialized. У Streamable HTTP сервер мог выдаць Mcp-Session-Id і звязаць наступныя паведамленні з гэтым злучэннем. У 2026-07-28 абмен initialize і загаловак пратакольнай сесіі выдалены. Кожны запыт абвяшчае версію пратакола і магчымасці кліента ў _meta. Кліенту варта перадаваць io.modelcontextprotocol/clientInfo, а серверу варта паказваць сваю ідэнтычнасць у метаданых выніку.

Параўнанне дапамагае спланаваць пераход:

Вобласць Паводзіны 2025 года Паводзіны 2026-07-28
Жыццёвы цыкл initialize і notifications/initialized Пратакольнага handshake няма
Сесія Неабавязковы Mcp-Session-Id у HTTP Пратакольнай сесіі няма
Магчымасці Узгадняюцца адзін раз Абвяшчаюцца ў кожным запыце
Выяўленне Пасля initialize або па дамоўленасці Сучасны сервер мусіць мець server/discover, кліент можа яго выклікаць
Сервер да кліента Запыты праз адкрыты канал MRTR вяртае запыты ўводу ў адказе
Маршрутызацыя HTTP Шлюз часта разбірае JSON Загалоўкі Mcp-Method і Mcp-Name пры патрэбе
Спісы і чытанне Свежасць вызначае кліент Падказкі ttlMs і cacheScope
Аднаўленне SSE мог выкарыстоўваць ID падзей Аднаўлення праз Last-Event-ID няма, патрэбен новы запыт
Рэгістрацыя DCR звычайна быў аўтаматычным шляхам Рэкамендуецца CIMD, DCR застаецца для сумяшчальнасці

Рэдакцыя пераносіць Tasks у пашырэнне io.modelcontextprotocol/tasks, замяняе стары паток змяненняў на subscriptions/listen і аб’яўляе Roots, Sampling, Logging ды стары HTTP+SSE састарэлымі. Палітыка дае мінімум дванаццаць месяцаў, але новыя рэалізацыі не павінны іх дадаваць.

Што азначае stateless на практыцы

У сучасным Streamable HTTP сервер публікуе адзін MCP endpoint, які прымае POST. Кліент адпраўляе адзін JSON-RPC-запыт або апавяшчэнне на кожны POST. Адказам можа быць адзін JSON-аб’ект або SSE-паток, звязаны толькі з гэтым запытам. Сервер не стварае ідэнтыфікатар сесіі, а перарваны паток не мае гісторыі для аднаўлення. У HTTP закрыццё патоку адказу з’яўляецца сігналам скасавання.

Загалоўкі транспарту і цела апісваюць адну аперацыю. Мінімальны выклік інструмента выглядае так:

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

{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search","arguments":{"q":"otters"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{},"io.modelcontextprotocol/clientInfo":{"name":"catalog-app","version":"1.0.0"}}}}

Значэнне MCP-Protocol-Version павінна супадаць са значэннем у _meta. Сучасны сервер адхіляе адсутны або несупадальны абавязковы загаловак з HTTP 400 і кодам HeaderMismatch, -32020. Пасля працы шлюза сервер павінен праверыць загалоўкі яшчэ раз. Гэта не дазваляе проксі накіраваць адзін інструмент, калі праграма выконвае іншы.

Stateless змяняе маштабаванне, але не бізнес-семантыку. Калі workflow павінен працягвацца, вярніце з інструмента відавочны handle і патрабуйце яго ў наступным выкліку. Захоўвайце аўтарытэтны стан у базе або workflow-сэрвісе, звяжыце handle з карыстальнікам і аперацыяй, задайце тэрмін дзеяння. Неправераны handle, requestState або аргумент інструмента не з’яўляецца доказам дазволу.

server/discover і сумяшчальнасць версій

Кожны сучасны сервер павінен рэалізаваць RPC server/discover. Яго вынік абвяшчае падтрыманыя версіі і магчымасці, а таксама можа ўтрымліваць неабавязковыя інструкцыі. Кліент можа выклікаць яго першым, каб выбраць версію, або адразу адправіць сучасны запыт. Таму discovery карысны, але не з’яўляецца абавязковым handshake на баку кліента.

Калі запытаная версія не падтрымліваецца, сервер вяртае UnsupportedProtocolVersionError са спісам версій. Кліент выбірае агульную версію і паўтарае запыт. Кліент, які падтрымлівае абедзве эпохі, павінен асцярожна класіфікаваць пробны запыт. Пусты адказ 400 або 400 без вядомай сучаснай JSON-RPC-памылкі можа азначаць старую кропку ўваходу. Вядомая сучасная памылка азначае, што запыт трэба выправіць або ўзгадніць зноў. Памылкі аўтарызацыі і інфраструктуры не даказваюць, што сервер стары.

Сучасны SDK можа праверыць злучэнне standard input і замацаваць эпоху за ім. Сервер можа пакінуць legacy-маршрут, пакуль новыя кліенты выкарыстоўваюць сучасныя запыты. Не вызначайце эпоху толькі па паспяховым TCP-злучэнні або агульным 404.

MRTR замяняе запыты сервера да кліента

Сучасны фармат выдаляе канал JSON-RPC-запытаў ад сервера да кліента. Інструменту, якому патрэбныя пацвярджэнне, адсутнае значэнне або крок з удзелам мадэлі, не трэба трымаць паток адкрытым. Ён вяртае прамежкавы вынік з resultType: "input_required" і картай inputRequests. Кліент адказвае і паўтарае зыходны метад з inputResponses. Паўтор з’яўляецца новым запытам і можа трапіць на іншую рэпліку.

Паток пацвярджэння можа мець такі выгляд:

{
  "resultType": "input_required",
  "inputRequests": {
    "confirm": {
      "type": "elicitation",
      "message": "Delete three files?",
      "schema": {"type": "boolean"}
    }
  },
  "requestState": "signed-opaque-state"
}

Потым кліент адпраўляе inputResponses.confirm і вяртае requestState пабайтна. Сервер павінен зноў увайсці ў апрацоўшчык як у новы запыт. Зрабіце апрацоўшчык ідэмпатэнтным, выводзьце бягучы крок з праверанага стану і запытвайце толькі яшчэ адсутныя даныя. Не пазначайце разбуральнае дзеянне завершаным да праверкі пацвярджэння.

requestState не з’яўляецца абароненым кантэйнерам. Ён праходзіць праз кліента, таму яго трэба лічыць уводам, якім можа кіраваць зламыснік. Падпісвайце яго HMAC або выкарыстоўвайце аўтэнтыфікаванае шыфраванне, звяжыце з principal, зыходным метадам, істотнымі параметрамі і тэрмінам дзеяння, а падробку адхіляйце да запуску апрацоўшчыка. Подпіс не хавае змесціва, таму не захоўвайце там сакрэты. TypeScript SDK дае codec стану запыту і hook праверкі. Яго legacy shim можа ператварыць той жа апрацоўшчык input_required у старыя elicitation/create, sampling/createMessage і roots/list, пакуль падтрымліваюцца кліенты 2025 года.

Для доўгіх аперацый выкарыстоўвайце Tasks з доўгажывучым task handle, апытаннем tasks/get і tasks/update, калі патрэбны ўвод кліента.

Падказкі кэшу і дэтэрмінаваныя каталогі

Сучасныя вынікі tools/list, prompts/list, resources/list, resources/templates/list і resources/read утрымліваюць ttlMs і cacheScope. ttlMs — неадмоўная падказка свежасці ў мілісекундах, падобная да HTTP max-age. Нуль азначае неадкладнае састарэнне. Для старых сервераў адсутнае значэнне трэба лічыць нулём. Дадатнае значэнне кажа кліенту, калі можна не выконваць новае атрыманне, але не гарантуе нязменнасць даных. Правярайце свежасць, калі даныя патрэбныя, і не ператварайце TTL у бесперапынны polling.

Ключ кэшу павінен уключаць метад і кожны параметр, які ўплывае на вынік, у тым ліку URI рэсурсу і cursor старонкавага спісу. Не кэшыруйце адказ з inputResponses або requestState, бо просты ключ спісу не ўтрымлівае яго кантэксту. cacheScope: "public" дазваляе дзяліцца вынікам паміж кантэкстамі аўтарызацыі, таму выкарыстоўвайце яго толькі для даных без карыстальніцкіх або permission-залежных палёў. Нават кэшаваны каталог не адмяняе аўтарызацыю кожнага інструмента.

Серверу варта вяртаць інструменты ў дэтэрмінаваным парадку. Стабільны парадак захоўвае нязменнасць prompt мадэлі і паляпшае паўторнае выкарыстанне кэшу пасля перападключэння. Апавяшчэнне listChanged можа дапоўніць TTL, але не замяняе правільную падказку.

OAuth і змены бяспекі

Аўтарызацыя для MCP у цэлым неабавязковая. HTTP-сервер, які абараняе рэсурсы, павінен прытрымлівацца профілю OAuth 2.1 са спецыфікацыі 2026 года. Ён працуе як resource server і мусіць публікаваць OAuth Protected Resource Metadata паводле RFC 9728. Адказ 401 павінен спасылацца на гэтыя метаданыя праз WWW-Authenticate і, калі дарэчы, утрымліваць scope challenge. Кліент павінен падтрымліваць URL з загалоўка і абедзве well-known формы.

Метаданыя могуць назваць некалькі authorization server. Кліенты павінны падтрымліваць OAuth Authorization Server Metadata паводле RFC 8414 і OpenID Connect Discovery. Для issuer са шляхам спачатку правярайце формы з устаўленым шляхам, потым OpenID-форму з дададзеным шляхам.

Рэгістрацыя цяпер аддае перавагу Client ID Metadata Documents. Папярэдняя рэгістрацыя таксама дапушчальная. Dynamic Client Registration з’яўляецца састарэлым запасным механізмам. Калі выкарыстоўваецца DCR, перадавайце правільны application_type для desktop або CLI-кліента. Правярайце PKCE, выкарыстоўвайце S256, рэгіструйце дакладныя redirect URI і ўжывайце HTTPS, акрамя дазволенага localhost callback.

Кліент павінен захаваць правераны issuer разам з транзакцыяй PKCE. Калі адказ аўтарызацыі ўтрымлівае iss, параўнайце яго да абмену кода. Уліковыя даныя прывязаныя да issuer, які іх выдаў, і не могуць паўторна выкарыстоўвацца з іншым authorization server. Перадавайце кананічны URI MCP-сервера як resource RFC 8707 у запытах аўтарызацыі і токена. Сервер мусіць правяраць audience і адхіляць токены іншага рэсурсу.

У Streamable HTTP правярайце Origin, каб блакаваць DNS rebinding. Лакальны сервер павінен слухаць localhost, а не ўсе інтэрфейсы. Не запісвайце bearer-токены ў query string або логі, патрабуйце згоды перад раскрыццём прыватных рэсурсаў і выклікам інструментаў, а апісанні ды annotations лічыце ненадзейнымі, калі сервер не правераны. 401 азначае адсутную або несапраўдную аўтарызацыю. 403 азначае недастатковыя дазволы і па магчымасці павінен утрымліваць insufficient_scope.

Міграцыя TypeScript SDK

TypeScript SDK v2 падзяляе пакеты кліента, сервера, ядра і runtime. Прачытайце даведнік SDK для 2026-07-28 і даведнік пераходу з v1 на v2. Адно абнаўленне не гарантуе сучасныя байты на дрот. Кліент v2 па змаўчанні ўзгадняе legacy-рэжым, таму відавочна ўключыце versionNegotiation.

Для кліента, які падтрымлівае абедзве эпохі, дакументацыя паказвае такую форму:

import { Client } from '@modelcontextprotocol/client';

const client = new Client(
  { name: 'catalog-app', version: '1.0.0' },
  { versionNegotiation: { mode: 'auto' } },
);
await client.connect(transport);

mode: 'auto' правярае server/discover і вяртаецца да handshake 2025 года толькі са сапраўды legacy-пірам. Зафіксуйце 2026-07-28, калі fallback можа схаваць несумяшчальнасць. createMcpHandler(factory) стварае сучасны HTTP-сервер для кожнага запыту і можа абслугоўваць абедзве эпохі. Для выбару эпохі ў stdio выкарыстоўвайце serveStdio(() => buildServer()).

Заменіце рэгістрацыю v1-апрацоўшчыкаў праз схемы на радкі метадаў, напрыклад setRequestHandler('tools/call', handler). Код, які чытае ctx.sessionId, заменіце application handles або правераным requestState. Push-style elicitation заменіце на inputRequired(...). Сучасны запыт не дасылае log-апавяшчэнне без io.modelcontextprotocol/logLevel. Codemod SDK — механічная дапамога, а не тэст сумяшчальнасці.

Для праверкі запускайце createMcpHandler праз fetch-падобны тэставы транспарт і асобна правярайце legacy-handshake. Тэстуйце загалоўкі, _meta, ідэмпатэнтнасць паўтораў, вобласць кэшу, audience і HTTP-статус рэальнымі старым і сучасным кліентамі.

Чэк-ліст міграцыі

  1. Складзіце спіс кліентаў, сервераў, транспартаў, сховішчаў сесій, SSE event store і чытання Mcp-Session-Id.
  2. Вызначце endpoint для сучаснага трафіку і маршрут, які часова застанецца legacy.
  3. Абнавіце SDK і зафіксуйце фактычныя версіі ў lockfile.
  4. Дадайце server/discover і палітыку ўзгаднення версій.
  5. Зрабіце кожны запыт самадастатковым і правярайце _meta ды люстраныя загалоўкі.
  6. Заменіце стан па сесіі відавочнымі handles або падпісаным requestState з тэрмінам дзеяння.
  7. Перапішыце ўзаемадзеянне сервера праз MRTR і зрабіце паўторы бяспечнымі.
  8. Дадайце ttlMs, cacheScope і дэтэрмінаваны парадак каталогам і рэсурсам.
  9. Абнавіце шлюзы, WAF, метрыкі і трасіроўку для Mcp-Method і Mcp-Name.
  10. Рэалізуйце праверкі issuer, resource, audience, PKCE, redirect, Origin і scopes.
  11. Не дадавайце HTTP+SSE, Roots, Sampling, Logging або DCR у новы код.
  12. Разгортвайце паступова з observability, параўноўвайце памылкі modern і legacy, а код сумяшчальнасці выдаляйце пасля пераходу спажыўцоў.

Дыягностыка

Сімптом Верагодная прычына Дзеянне
HTTP 400 з -32020 Загаловак адсутнічае або не супадае з целам Пералічыце MCP-Protocol-Version, Mcp-Method і Mcp-Name з аднаго аб’екта
HTTP 400 і памылка версіі Пір не падтрымлівае патрэбную рэдакцыю Выберыце версію з supported або legacy-маршрут
HTTP 404 і method-not-found Endpoint сучасны, але метаду няма Праверце метад і пашырэнне, не запускайце initialize наўздагад
HTTP 401 або 403 падчас discovery Аўтарызацыя заблакавала пробу Выпраўце credentials і метаданыя, гэты статус не даказвае legacy
Няма log-апавяшчэнняў Няма io.modelcontextprotocol/logLevel Уключыце яго для запыту або выкарыстоўвайце stderr і OpenTelemetry
Паўторныя пабочныя эфекты Паток больш нельга аднавіць Выкарыстоўвайце idempotency keys і новы ID запыту
Даныя карыстальніка трапляюць у іншы кэш Вынік памылкова пазначаны public Выкарыстайце private і захавайце праверку principal
Стары кліент атрымлівае 405 на GET Ён чакае HTTP+SSE Часова пакіньце legacy-маршрут

Кантэкст архітэктуры ёсць у матэрыялах production AI agent architecture і MCP server with TypeScript and OAuth.

Крыніцы

Матэрыял грунтуецца на спецыфікацыі MCP 2026-07-28, журнале змяненняў і састарэлых магчымасцяў, патрабаваннях Streamable HTTP, SEP-2575 пра MCP без стану, артыкуле пра рэліз 2026-07-28, спецыфікацыі аўтарызацыі MCP і дакументацыі міграцыі TypeScript SDK.

Іншыя публікацыі