MCP по спецификации: что на самом деле такое сервер
Одна строка JSON в подпроцесс — и назад приходят 13 определений инструментов, по ревизии 2026-07-28 без handshake.
На этой странице
Установите опубликованный MCP-сервер, отправьте ему одну строку JSON и прочитайте, что вернётся.
npm i @modelcontextprotocol/server-everything@2026.8.31
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| npx @modelcontextprotocol/server-everything stdio{"result":{"tools":[{"name":"echo","title":"Echo Tool","description":"Echoes
back the input string","inputSchema":{"$schema":"http://json-schema.org/draft-07/
schema#","type":"object","properties":{"message":{"type":"string","description":
"Message to echo"}},"required":["message"]},"annotations":{"readOnlyHint":true,
… … 7,663 bytes on one line …
"jsonrpc":"2.0","id":1}Тринадцать определений инструментов, одной строкой, от процесса, который прочитал одну строку из своего стандартного ввода. Вы только что поговорили на Model Context Protocol — без SDK, без клиентской библиотеки и без фреймворка. Вот и всё: транспорт, формат сообщений и небольшой набор именованных методов.
Глава 18 определила инструмент как две вещи: JSON Schema, которую видит модель, и endpoint в вашем коде, который модель никогда не видит. Глава 23 построила harness, который держит их каталог. Ни одна из них не ответила на вопрос, от которого зависит, можно ли всё это переиспользовать: кто пишет схему и как она попадает от автора в ваш prompt? MCP — один из ответов на этот вопрос, и его стоит читать в оригинале, потому что почти всё написанное о нём описывает ревизию, которой больше нет.
В команде, которую вы только что запустили, неверны три вещи, и каждая из них — раздел этой главы. В ней не было версии протокола, поэтому совместимый сервер должен был бы её отклонить. Но ответ всё равно пришёл — по причине, которую спецификация называет не возможностью, а опасностью. И команда запросила один из трёх примитивов, так и не обнаружив, что существуют ещё два.
Проблема, которую он решает, и аналогия, которую сама проводит спецификация
Ссылка на раздел: Проблема, которую он решает, и аналогия, которую сама проводит спецификацияДо проводов — арифметика. У вас есть AI-приложений и вещей, до которых они должны уметь дотянуться: календарь, трекер задач, база данных склада, инструмент дизайна. Без общего контракта кто-то пишет интеграций, и каждая из них — это схема плюс endpoint плюс история с аутентификацией плюс нагрузка на поддержку. С контрактом поставщик инструмента пишет сервер, поставщик приложения пишет клиент, а итог — .
Это не новое наблюдение, и спецификация прямо говорит, чья это идея:
MCP takes some inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across a whole ecosystem of development tools. In a similar way, MCP standardizes how to integrate additional context and tools into the ecosystem of AI applications.1
Воспринимайте это сравнение буквально, а не как комплимент. До того протокола поддержка языка в редакторе означала плагин для каждого редактора; после него команда языка поставляла один сервер, и каждый редактор получал поддержку. Мерой успеха была не элегантность, а то, что число интеграций перестало перемножаться. Здесь следует то же самое: ценность — в количестве реализаций, а не в дизайне. Протокол, на котором говорят два продукта, — это формат данных с дополнительной церемонией.
Что на самом деле идёт по проводу
Ссылка на раздел: Что на самом деле идёт по проводуСообщения MCP — это JSON-RPC 2.0. Запрос — объект с jsonrpc, id, method и необязательным params; ответ несёт тот же id и либо result, либо error; уведомление — это запрос без id, и ответа на него нет. Спецификация добавляет поверх этого три ограничения: id должен быть строкой или числом и не должен быть null, он не должен совпадать с другим запросом, который ещё в работе, и каждый результат должен нести поле resultType.2
На транспорте stdio — именно его использовала команда выше — правило фрейминга такое: одна строка на сообщение:
Messages are delimited by newlines, and MUST NOT contain embedded newlines. […] The server MUST NOT write anything to its
stdoutthat is not a valid MCP message.3
Последний пункт — самый частый способ сломать самодельный сервер, причём ломается он тихо: случайный console.log, индикатор прогресса, предупреждение об устаревании от зависимости — и построчный парсер клиента натыкается на что-то, что не является JSON. Выход есть в том же разделе: сервер может писать в stderr всё что угодно, а клиент не должен считать это ошибкой. Референсный сервер выше при каждом запуске печатает Starting default (STDIO) server... в stderr, поэтому pipe всё равно сработал.
Другой стандартный транспорт — Streamable HTTP: каждое сообщение — POST в единый endpoint, а ответ — либо JSON-объект, либо привязанный к запросу поток Server-Sent Events, тот самый формат провода, который глава 14 разбирала вручную. Семантика на обоих транспортах одинакова, потому что транспорт — это привязка: он определяет фрейминг и доставку, а не смысл.4
Первое, что было неверно: версии не было
Ссылка на раздел: Первое, что было неверно: версии не былоКоманда выше отправила tools/list и ничего больше. В текущей ревизии такой запрос некорректен, и совместимый сервер обязан его отклонить.
С 2026-07-28 MCP — stateless-протокол, и спецификация говорит это без оговорок:
The Model Context Protocol (MCP) is a stateless protocol: all the information needed to process a request is contained in the request itself. A server processes each request independently; no state should be inferred from previous requests, even those on the same connection or stream.2
Поэтому каждый запрос несёт собственную версию протокола и собственные возможности клиента — в зарезервированном объекте _meta внутри params. Два из этих полей обязательны в каждом отдельном запросе; запрос без любого из них некорректен, и сервер обязан ответить -32602:2
ключ _meta | обязательно | что это |
|---|---|---|
io.modelcontextprotocol/protocolVersion | да | ревизия, на которой говорит этот запрос, например "2026-07-28" |
io.modelcontextprotocol/clientCapabilities | да | что клиент может сделать для сервера в этом запросе |
io.modelcontextprotocol/clientInfo | нет (но следует) | имя и версия клиента, только для отображения и логов |
io.modelcontextprotocol/logLevel | нет | минимальный уровень логирования, который серверу следует выдавать для этого запроса |
В полном виде корректный tools/list выглядит так — и это последний раз, когда глава показывает метаданные полностью, потому что дальше они есть в каждом запросе:
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{"elicitation":{"form":{}}},
"io.modelcontextprotocol/clientInfo":{"name":"bare-hands","version":"0.0.1"}}}}Объект capabilities и есть negotiation. Отдельного шага negotiation больше нет: клиент объявляет, что он умеет, в каждом запросе, сервер объявляет, что умеет, в результате, и ни одна сторона не может использовать функцию, которую другая не заявила. Сервер, которому нужна capability, не объявленная клиентом, обязан ответить -32021 и назвать недостающую capability в data.requiredCapabilities. Сервер, который не говорит на запрошенной версии, обязан ответить -32022 и перечислить версии, на которых он говорит.2
Клиенты, которые хотят получить ответ заранее, могут его запросить: server/discover — обязательный RPC, который за один round trip возвращает поддерживаемые версии, capabilities, идентичность и необязательный блок instructions.5 Вызывать его необязательно. Реализовывать — обязательно.
Второе, что было неверно: сервер был legacy
Ссылка на раздел: Второе, что было неверно: сервер был legacyКоманда сработала. В текущей ревизии она не должна была сработать, и причину стоит не описывать абзацем, а измерить, потому что это состояние всей экосистемы в одну строку.
Проверьте референсный сервер так, как спецификация велит современному клиенту проверять:
echo '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{}}}}' \
| npx @modelcontextprotocol/server-everything stdio{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}Это третья ветка правила совместимости: DiscoverResult означает modern, распознанная modern-ошибка означает modern-but-wrong-version, а что угодно другое — включая -32601 — означает legacy, откатитесь к handshake initialize.3 Так и сделаем, запросив текущую ревизию:
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28",
"capabilities":{},"clientInfo":{"name":"bare-hands","version":"0.0.1"}}}
← {"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{"listChanged":true},
"prompts":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},
"logging":{},"tasks":{…},"completions":{}},"serverInfo":{"name":"mcp-servers/everything",
"title":"Everything Reference Server","version":"2.0.0"},"instructions":"…"}}Клиент запросил 2026-07-28, а сервер ответил 2025-11-25. 7 сентября 2026 года официальный референсный сервер — npm-пакет @modelcontextprotocol/server-everything, версия 2026.8.31, опубликованный 31 августа 2026 года — не реализует текущую ревизию. Как, судя по датам, и TypeScript SDK, на котором он построен: релиз 1.30.0 вышел 27 июля 2026 года, за день до этой ревизии.
Читайте не сплетню, а следствие. Почти всё написанное о MCP описывает протокол с handshake initialize, сессией, запросом roots/list, который сервер отправляет клиенту, и транспортом HTTP+SSE. Все четыре уже исчезли или исчезают. Когда вы читаете что угодно про MCP, включая эту страницу, первое, что нужно искать, — номер ревизии.
А причина, по которой самая первая команда сработала, сформулирована в спецификации как опасность, а не возможность:
some legacy servers do not validate that a request arrives after
initializeand would process an era-ambiguous method (such astools/call) under legacy semantics. Probing yields a deterministic failure instead.3
Измерено: отправка tools/list этому серверу вообще без handshake возвращает полный каталог. Метод, который должен был быть отклонён, был обслужен — именно поэтому спецификация велит сначала проверять через server/discover, даже если вы поддерживаете только modern-версии.
Три роли и фраза, которую стоит цитировать из всего документа
Ссылка на раздел: Три роли и фраза, которую стоит цитировать из всего документаВ MCP три стороны, и различие между первыми двумя люди чаще всего схлопывают:
Хост. Приложение: чат-продукт, редактор, agent. Ему принадлежат разговор, модель, credentials и согласие пользователя. Он создаёт клиентов и обеспечивает границу безопасности между ними.
Клиент. Коннектор внутри хоста. Каждый клиент говорит ровно с одним сервером — строгое отношение 1:1 — и прикрепляет версию протокола и capabilities к каждому запросу, который маршрутизирует.
Сервер. Процесс или сервис, который предоставляет ресурсы, инструменты и prompts. Он может быть локальным или удалённым, работает независимо, и вся его задача — одна сфокусированная область.6
Правило «ровно один сервер» — не бухгалтерия. Именно оно делает реализуемым принцип дизайна ниже, и вот фраза, которую стоит унести из спецификации, если брать только одну:
Servers should not be able to read the whole conversation, nor "see into" other servers. Servers receive only necessary contextual information. Full conversation history stays with the host. Each server maintains isolation. Cross-server interactions are controlled by the host.6
Это переворачивает ментальную модель, с которой приходит большинство людей. Сервер погоды, который вы подключаете к своему ассистенту, не видит, что вы спросили. Он видит tools/call с аргументами, которые выбрала модель, и ничего больше: ни предыдущих реплик, ни вашего system prompt, ни результатов, которые минуту назад вернул сервер календаря. Если двум серверам нужно взаимодействовать, хост намеренно переносит значение от одного к другому, потому что модель попросила это сделать. Поэтому изоляция — свойство безопасности, на которое опирается глава 30: скомпрометированный сервер имеет небольшой и определённый радиус поражения, а расширить его можно только при сотрудничестве хоста.
Третье: три примитива, отсортированные по тому, кто принимает решение
Ссылка на раздел: Третье: три примитива, отсортированные по тому, кто принимает решениеПервая команда запросила у сервера инструменты и получила тринадцать. Задайте ему два других вопроса, и он тоже ответит: resources/list возвращает семь, prompts/list возвращает четыре. Они не появились, потому что их никто не запросил. И это приводит нас к педагогическому позвоночнику MCP — таблице в спецификации, которую почти никто не цитирует:
| Primitive | Control | Description | Example |
|---|---|---|---|
| Prompts | User-controlled | Interactive templates invoked by user choice | Slash commands, menu options |
| Resources | Application-controlled | Contextual data attached and managed by the client | File contents, git history |
| Tools | Model-controlled | Functions exposed to the LLM to take actions | API POST requests, file writing |
Не «три способа предоставить capability». Три ответа на вопрос: кто решает, что это происходит. Модель решает вызвать инструмент. Приложение решает прикрепить ресурс. Человек решает запустить prompt. Ошибитесь в этом — и функция всё равно будет работать, но в неверный момент и по неверной причине.
Самый простой способ это почувствовать — календарь. Вот сервер, который предоставляет один и тот же календарь тремя способами, по одному на каждый примитив, в сотне строк обычного Node без зависимостей:
const TOOL = {
name: "create_event",
description: "Create a calendar event. Writes to the calendar.",
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "Event title." },
startsAt: { type: "string", format: "date-time", description: "Start, ISO 8601 UTC." },
},
required: ["title"],
},
};
switch (method) {
case "resources/read":
return ok(id, { contents: [{ uri: "calendar://week",
mimeType: "application/json", text: JSON.stringify(EVENTS) }],
ttlMs: 60000, cacheScope: "private" });
case "prompts/get":
return ok(id, { description: PROMPT.description, messages: [{ role: "user",
content: { type: "text", text: `Read calendar://week and draft a plan. ` +
`Focus: ${params.arguments?.focus ?? "balance"}.` } }] });
case "tools/list":
return ok(id, { tools: [TOOL], ttlMs: 300000, cacheScope: "public" });
}Запустите его и запросите все три способа. Реальный вывод, одно сообщение на строку по проводу, здесь перенесённый для страницы, с опущенными request _meta и блоком identity сервера:
→ resources/read {"uri":"calendar://week"}
← {"resultType":"complete","contents":[{"uri":"calendar://week",
"mimeType":"application/json","text":"[{\"id\":\"e1\",\"title\":\"Standup\",
\"startsAt\":\"2026-09-07T09:00:00Z\"},{\"id\":\"e2\",\"title\":\"Design review\",
\"startsAt\":\"2026-09-09T15:00:00Z\"}]"}],"ttlMs":60000,"cacheScope":"private"}
→ prompts/get {"name":"prepare_week","arguments":{"focus":"deep work"}}
← {"resultType":"complete","description":"Read the week and draft a plan.",
"messages":[{"role":"user","content":{"type":"text",
"text":"Read calendar://week and draft a plan. Focus: deep work."}}]}
→ tools/call {"name":"create_event","arguments":{"title":"Dentist",
"startsAt":"2026-09-10T08:30:00Z"}}
← {"resultType":"complete","content":[{"type":"text",
"text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],
"structuredContent":{"id":"e3","title":"Dentist","startsAt":"2026-09-10T08:30:00Z"},
"isError":false}Три метода, три формы, один календарь. Теперь суть:
Чтение недели — это ресурс
Ссылка на раздел: Чтение недели — это ресурсУ него есть URI, он инертен, и приложение решает, прикреплять ли его к разговору. В протоколе нет ничего, что позволило бы модели самой за ним потянуться. Результат несёт ttlMs и cacheScope, новые в этой ревизии, чтобы клиент мог кэшировать неделю минуту вместо polling.
Создание события — это инструмент
Ссылка на раздел: Создание события — это инструментУ него есть схема, у него есть побочные эффекты, и модель решает, когда его вызвать. Его результат несёт isError — то самое поле, за которое спорила глава 18: ошибка валидации возвращается как результат инструмента, который модель может прочитать и исправить, а не как ошибка протокола.
«Подготовь мою неделю» — это prompt
Ссылка на раздел: «Подготовь мою неделю» — это promptЭто именованный шаблон с аргументами, который вызывает человек — slash command в меню. Он возвращает messages, а не ответ. Это способ для автора сервера поставить формулировку, которая работает с его собственными инструментами, то есть ровно то знание, которое есть у автора сервера и которого нет у пользователя.
Почти все делают все три вещи инструментами. В результате получается каталог, где чтение, которое приложение должно было молча прикрепить, конкурирует за attention модели с записью, которой нужно подтверждение, а единственная вещь, для которой человек хотел кнопку, закопана в схеме. Сделать правильно ничего не стоит, и решение принимается до первой строки кода.
Сервер не может вам позвонить
Ссылка на раздел: Сервер не может вам позвонитьУ инструмента календаря есть один обязательный аргумент, title, и необязательный startsAt. Попросите создать событие без даты, и вернётся кое-что интересное:
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"}}
← {"resultType":"input_required",
"inputRequests":{"when":{"method":"elicitation/create","params":{"mode":"form",
"message":"When should \"Dentist\" start?",
"requestedSchema":{"type":"object",
"properties":{"startsAt":{"type":"string","format":"date-time"}},
"required":["startsAt"]}}}},
"requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}Сервер не отправил запрос. Он ответил на тот, который получил, с resultType: "input_required" и описанием того, что ему ещё нужно. Клиент собирает ответ у человека, а затем повторно отправляет исходный вызов — с новым id, неся inputResponses и возвращая непрозрачный requestState:
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"},
"inputResponses":{"when":{"action":"accept",
"content":{"startsAt":"2026-09-10T08:30:00Z"}}},
"requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}
← {"resultType":"complete","content":[{"type":"text",
"text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],"isError":false}Это Multi Round-Trip Requests, введённые в текущей ревизии; они заменили старый дизайн, где серверы отправляли JSON-RPC-запросы обратно клиентам. Спецификация транспорта теперь формулирует правило прямо: «servers do not initiate JSON-RPC requests and clients do not send JSON-RPC responses».4 Есть одно направление инициативы, и оно принадлежит хосту.
На этом механизме едут две клиентские функции, и у одной из них название, которое вас собьёт.
Elicitation — это когда сервер просит что-то у человека: форму с намеренно ограниченной JSON Schema — плоские объекты, примитивные свойства, без вложенности, — чтобы любой клиент мог отрисовать её без layout engine. У него есть жёсткое правило: серверы не должны использовать form mode, чтобы запрашивать «passwords, API keys, access tokens, or payment credentials», и обязаны использовать URL mode для таких данных; он отправляет пользователя на страницу, которую клиент никогда не читает.7
Sampling — это когда сервер просит модель хоста сгенерировать ответ, чтобы сервер мог быть интеллектуальным без собственного API key. И вот предупреждение по словарю, потому что это слово уже означает в курсе другое: это не sampling из главы 17. Здесь ничего нет про temperature, top-p или форму распределения вероятностей. Это вложенный вызов модели, который идёт назад через протокол.
Есть и вторая причина не тянуться к нему: на момент этой ревизии sampling устарел, вместе с roots и logging, в рамках SEP-2577, с прямолинейной рекомендуемой миграцией — «integrate directly with LLM provider APIs instead of Sampling».8 Идея не провалилась технически; она не оправдала свою площадь поверхности, а протокол, который умеет удалять вещи, здоровее протокола, который не умеет.
Сломайте специально: соединения не являются сессиями
Ссылка на раздел: Сломайте специально: соединения не являются сессиямиStatelessness звучит как деталь формата провода, пока вы это не проверите. Возьмите трёхсообщенческий обмен выше и выполните каждое сообщение в отдельном процессе — свежий node calendar.mjs, без общей памяти, без переноса состояния:
process A tools/call (no date) → resultType: input_required
requestState: eyJ0aXRsZSI6IkRlbnRpc3QifQ==
process B tools/call (with the answer, same requestState)
→ resultType: complete
"Created e3: Dentist at 2026-09-10T08:30:00Z"
process C resources/read calendar://week
→ events: 2 (Standup, Design review)Процесс B, который никогда не видел вопроса, завершил multi-round-trip-вызов, начатый процессом A. В этом и смысл requestState: continuation едет в сообщении, поэтому ничто не зависит от того, тот же ли это процесс.
Процесс C — это сбой. Событие было создано, но его там нет, потому что игрушечный сервер держит EVENTS в массиве уровня модуля, а массив уровня модуля — это состояние соединения. Примечание спецификации называет ошибку точно:
an open connection, such as a STDIO process, is not a conversation or session: clients may interleave unrelated requests on the same transport, and a server must not treat connection or process identity as a proxy for conversation or session continuity.2
Предписанное исправление — не сессия. Это явный handle: инструмент создания возвращает непрозрачный идентификатор, а каждый последующий вызов принимает его как обычный аргумент. У протокола вообще нет такого понятия: «from the wire's perspective a handle is an ordinary string in a tool result and an ordinary argument to subsequent tool calls».9 Это отдаёт модели ответственность за перенос handle, а серверу — за проверку, что этому вызывающему разрешено использовать его в каждом отдельном вызове, потому что handle — это имя, а не разрешение.
Сколько сервер стоит до того, как что-либо сделает
Ссылка на раздел: Сколько сервер стоит до того, как что-либо сделаетКаждый инструмент, который предоставляет сервер, — это схема, попадающая в ваш prompt при каждом запросе, а глава 24 измерила, что это делает с окном. MCP добавляет вторую строку расходов, которую легко пропустить, поэтому стоит посчитать обе на референсном сервере выше.
13 tool definitions (name + description + inputSchema): 1,307 tokens
cheapest tool, get-tiny-image 52
costliest tool, gzip-file-as-resource 235
server `instructions`, returned by discovery: 312 tokens
------
one server, connected, before it is used: 1,619 tokensДва наблюдения. Первое — арифметическое: подключите пять серверов такого размера, и примерно восемь тысяч token вашего окна будут заняты на каждом turn, всегда, независимо от того, использует ли модель хоть один из них. Именно этот механизм стоит за сокращением со 150 000 до 2 000, которое цитировала глава 24, и именно поэтому существует just-in-time discovery инструментов.
Второе — заметка о безопасности в костюме бухгалтерии. instructions — это текст на естественном языке, написанный автором сервера, который попадает в prompt хоста, и описания инструментов рядом с ним — то же самое. Спецификация в собственных принципах безопасности говорит, что с этим делать: аннотации и описания инструментов «should be considered untrusted, unless obtained from a trusted server», а хосты «must obtain explicit user consent before invoking any tool».1 Подключить MCP-сервер — не значит добавить зависимость. Это значит выдать незнакомцу 1 619 token вашего system prompt и право быть вызванным. Глава 30 — о том, что происходит, когда этот незнакомец враждебен.
Раздел с датой: ревизия 2026-07-28 и что она ломает
Ссылка на раздел: Раздел с датой: ревизия 2026-07-28 и что она ломаетВсё в этом разделе верно для ревизии протокола 2026-07-28, текущей на момент чтения 7 сентября 2026 года. Ревизии датируются YYYY-MM-DD, и дата — это последний момент, когда было сделано обратно несовместимое изменение.10 Нормативный документ — TypeScript-файл, schema/2026-07-28/schema.ts; JSON Schema рядом с ним генерируется из него, поэтому здесь спецификация читается в TypeScript, и поэтому учить MCP по чему-то ещё — значит учить перевод.
| Что изменилось | Было | Теперь | Что ломает |
|---|---|---|---|
| Handshake | initialize + notifications/initialized, один раз на соединение | удалён; каждый запрос несёт версию _meta и capabilities | каждый клиент, написанный до этой ревизии |
| Сессии | заголовок Mcp-Session-Id, состояние в рамках соединения | удалены; состояние едет в явных handle, созданных сервером | list endpoints, которые менялись от соединения к соединению |
| Discovery | выводился из результата initialize | server/discover, который серверы обязаны реализовать | ничего, но теперь это обязательно реализовывать |
| Вызовы server-to-client | сервер отправлял roots/list, sampling/createMessage, elicitation/create | InputRequiredResult и повтор клиента | каждый сервер, который пушил запрос клиенту |
| Форма результата | любой объект | обязательный resultType: "complete" или "input_required" | ничего: отсутствующее поле нужно читать как "complete" |
| Subscriptions | HTTP GET stream, resources/subscribe | один stream subscriptions/listen с opt-in-типами | GET endpoint исчез |
| Возобновление stream | replay Last-Event-ID на Streamable HTTP | удалено; сломанный stream теряет запрос, повторите с новым id | клиенты, которые полагались на повторную доставку |
| Roots | клиентская функция, о которой серверы могли попросить | deprecated (SEP-2577); передавайте пути как аргументы инструмента или resource URIs | пока ничего — окно двенадцать месяцев |
| Sampling и logging | клиентские функции | deprecated (SEP-2577) | пока ничего — окно двенадцать месяцев |
| Транспорт HTTP+SSE | deprecated с 2025-03-26 | Deprecated по lifecycle policy (SEP-2596) | мигрируйте на Streamable HTTP |
| Регистрация клиента | OAuth 2.0 Dynamic Client Registration, RFC 7591 | deprecated в пользу Client ID Metadata Documents | сохранено для authorization servers без них |
| Коды ошибок | -32002 для resource not found | -32602; -32020–-32099 зарезервированы для спецификации | новые коды -32020, -32021, -32022 |
Изменение управления под этой таблицей важнее любой отдельной строки. Эта ревизия приняла feature lifecycle and deprecation policy: функции бывают Active, Deprecated или Removed; deprecated-функция документирует путь миграции и остаётся в спецификации минимум двенадцать месяцев, прежде чем становится кандидатом на удаление; есть реестр всего, что сейчас находится в состоянии Deprecated.8 До этой политики «deprecated» в AI-протоколе означало то, что сказал последний blog post. Теперь это означает дату.
Показать детали
Extensions — часть, о которой пока почти никто не написал.
Помимо ядра MCP определяет необязательные extensions — «always opt-in and require explicit support from both client and server», объявляемые через поле extensions в capabilities клиента и сервера.1 Три стоит знать по имени:
- Tasks (
io.modelcontextprotocol/tasks), вынесенные из ядра протокола в официальное extension в этой ревизии: асинхронное выполнение долгих операций, polling черезtasks/get, input в середине выполнения черезtasks/updateи durable handles. Это ответ на инструмент, который занимает двадцать минут; глава 23 решала это progress event и сигналом, доходящим до инструмента. - Skills over MCP, рабочая группа, делающая agent skills — тему главы 28 — обнаруживаемыми и потребляемыми через протокол.
- MCP Apps, интерактивный UI, отрисованный inline в разговоре: графики, формы, видеоплееры.
И обратите внимание, что теперь означает «negotiated»: initialization, в котором можно было бы negotiation, нет, поэтому extension объявляется на каждый запрос, как и всё остальное.
Где MCP находится относительно всего, с чем его путают
Ссылка на раздел: Где MCP находится относительно всего, с чем его путаютВот словарь всего блока в одном месте.
| Что это | Кто с кем говорит | Когда это ответ | |
|---|---|---|---|
| Обычный API | Интерфейс для программы | ваш код ↔ сервис | Вы пишете вызывающую сторону. Вы контролируете схему, auth и обработку ошибок, и проблемы discovery решать не нужно. |
| MCP | Протокол для предоставления инструментов, данных и шаблонов AI-приложению | хост ↔ сервер, по одному клиенту на каждый | Кто-то другой написал capability, и многие хосты должны уметь использовать её без bespoke-интеграции. |
| RAG | Техника поиска текста и помещения его в prompt | ваш код ↔ ваш индекс | Модели нужно что-то знать. Глава 19. MCP — способ доставить retriever; он не является retriever. |
| Agent skills | Папка с SKILL.md, которую модель читает | модель ↔ документ | Знание процедурное — как мы это делаем — и это проза, а не функция. Глава 28. |
| A2A | Протокол для сотрудничества agents как равных | agent ↔ agent | На другой стороне reasoning, planning и состояние на протяжении долгой задачи, а не ответ на вызов. |
| ACP | Был отдельным протоколом коммуникации agents | — | Это больше не живое сравнение. См. ниже. |
Двум из них нужно по предложению, потому что именно там реально живёт путаница.
MCP против A2A — не соперничество, и обе спецификации это говорят. Документация A2A проводит границу по тому, что находится на другом конце: MCP «defines how an AI agent interacts with and utilizes individual tools and resources, such as a database or an API», где инструмент выполняет «specific, often stateless, functions»; A2A адресован agents, «more autonomous systems», которые «reason, plan, use multiple tools, maintain state over longer interactions, and engage in complex, often multi-turn dialogues». Его собственное резюме — фраза, которую нужно запомнить: «A2A is about agents partnering on tasks, while MCP is more about agents using capabilities».11 Они вкладываются друг в друга: приложение использует A2A, чтобы достучаться до других agents, а каждый agent использует MCP, чтобы достучаться до собственных инструментов. Глава 25 проводила эту линию внутри одного процесса — между тем, чтобы попросить sub-agent и передать ему разговор; A2A проводит её между организациями.
MCP против ACP — сравнение с устаревшей предпосылкой, и именно поэтому на него стоит ответить. Agent Communication Protocol был отдельным открытым стандартом для обмена сообщениями между agents. Его собственная документация теперь начинается с уведомления: «ACP is now part of A2A under the Linux Foundation!»12 Честный ответ на «MCP или ACP?» в сентябре 2026 года: у вопроса на один вариант меньше, чем утверждают страницы, которые по нему ранжируются.
А сравнение, которое спрашивают чаще всего, mcp vs api, имеет самый скучный ответ: MCP — это API. Он добавляет не мощь, а соглашения: фиксированный набор имён методов, discovery-вызов, иерархию управления примитивами и модель изоляции. Вы отказываетесь от свободы проектировать собственный интерфейс и получаете каждый хост, который говорит на протоколе; это сделка, которую всегда предлагал любой протокол.
Куда дальше
Ссылка на раздел: Куда дальшеТеперь вы можете читать спецификацию без переводчика, отличать ресурс от инструмента и от prompt по тому, кто им управляет, набрать запрос вручную, когда клиентская библиотека вам врёт, и датировать любую статью про MCP по тому, какие deprecated-функции она всё ещё преподаёт как актуальные.
Чего вы ещё не сделали — не поставили сервер в production. Глава 27 пишет один и тот же сервер дважды: TypeScript и Python, рядом, потому что MCP — единственная по-настоящему двуязычная территория в этом курсе, и цифры говорят это в обе стороны. Она корректно покрывает два живых транспорта, inspector, packaging и половину протокола, которую эта глава намеренно оставила в стороне: authorization. Потому что как только ваш сервер становится удалённым, а не подпроцессом на вашем ноутбуке, чужой клиент предъявит token, и правило спецификации о том, что вам разрешено с ним делать, необычно строгое.
Отсюда вопрос, на который должна ответить следующая глава, и он недружелюбный: если token приходит на ваш сервер и был выпущен для чужой audience, что именно мешает вам переслать его дальше?
Источники и метод
Ссылка на раздел: Источники и методКаждая цитата, имя метода, код ошибки и правило в этой главе прочитаны из спецификации Model Context Protocol, ревизия 2026-07-28, 7 сентября 2026 года. Каждый trace был произведён локально на Node 22: игрушечный сервер календаря — 101 строка без зависимостей, а референсный сервер — опубликованный npm-пакет, названный ниже. Для написания этой главы не вызывался платный API — здесь ничего не требует модели, и это само по себе часть смысла.
Измерения: @modelcontextprotocol/server-everything@2026.8.31, опубликован 31 августа 2026 года, построен на @modelcontextprotocol/sdk@1.30.0, опубликованном 27 июля 2026 года — за день до ревизии, которую описывает эта глава. Он отвечает на server/discover через -32601, договаривается о 2025-11-25 при запросе 2026-07-28 и обслуживает tools/list вообще без handshake. Его каталог — 13 инструментов в 7 663 байтах; подсчёты token — o200k_base через tiktoken, по name, description и inputSchema каждого определения, то есть по тому, что провайдер рендерит в ваш prompt, а не по весу JSON-RPC frame.
Anthropic, Code execution with MCP: building more efficient agents, 4 November 2025, — источник числа 150 000-to-2 000, процитированного и использованного в главе 24 и здесь только упомянутого.
Сноски
Ссылка на раздел: Сноски-
Specification,
modelcontextprotocol.io/specification/latest(redirecting to/2026-07-28), прочитано 7 сентября 2026 года. Источник сравнения с Language Server Protocol; утверждения, что спецификация «based on the TypeScript schema inschema.ts»; краткого описания базового протокола («Stateless, self-contained requests», «Per-request capability negotiation»); списка extensions (Tasks, Skills over MCP, MCP Apps) и утверждения, что extensions «are always opt-in and require explicit support from both client and server»; а также принципов Security и Trust & Safety, включая «Hosts must obtain explicit user consent before invoking any tool» и трактовку аннотаций инструментов как untrusted. ↩ ↩2 ↩3 -
Base Protocol,
modelcontextprotocol.io/specification/2026-07-28/basic. Источник ограничений JSON-RPC (non-null id, запрет повторного использования id, обязательныйresultType); раздела Statelessness и его примечания, что открытый stdio-процесс не является сессией; таблицы зарезервированных ключей_metaи статуса required/optional каждого поля per-request; правила-32602для отсутствующего обязательного поля; правилаMissingRequiredClientCapability(-32021); и политики распределения кодов ошибок. ↩ ↩2 ↩3 ↩4 ↩5 -
stdio transport,
modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio. Источник правил framing с разделением по newline, требования чистотыstdout, разрешенияstderrи трёхисходного backward-compatibility probe — включая предупреждение, что некоторые legacy-серверы обрабатывают era-ambiguous methods без handshake, что измерение в этой главе воспроизводит. ↩ ↩2 ↩3 -
Transports overview,
modelcontextprotocol.io/specification/2026-07-28/basic/transports. Источник формулировки «a transport is a binding» и утверждения, что серверы не инициируют JSON-RPC-запросы, а клиенты не отправляют JSON-RPC-ответы. ↩ ↩2 -
Discovery,
modelcontextprotocol.io/specification/2026-07-28/server/discover. Источник обязательного статусаserver/discover, формыDiscoverResultи поляinstructions, описанного как «optional natural-language guidance for LLMs on how to use this server effectively». ↩ -
Architecture,
modelcontextprotocol.io/specification/2026-07-28/architecture. Источник определений host/client/server, правила 1:1 client-to-server, четырёх принципов дизайна, из которых принцип изоляции цитируется здесь без пятого пункта, «Host process enforces security boundaries», и раздела capability negotiation. ↩ ↩2 -
Elicitation,
.../client/elicitation, и Sampling,.../client/sampling. Источник двух режимов elicitation и их ограниченной схемы; запрета на запрос credentials через form mode; определения sampling, его требования human-in-the-loop и deprecation warning, прикреплённого к нему. ↩ -
Key Changes,
modelcontextprotocol.io/specification/2026-07-28/changelog, и Feature lifecycle and deprecation policy,.../community/feature-lifecycle. Источник каждой строки таблицы изменений: удаления сессий и заголовкаMcp-Session-Id(SEP-2567); statelessness и удаленияinitialize(SEP-2575);server/discover(SEP-2575);subscriptions/listen(SEP-2575); Multi Round-Trip Requests иresultType(SEP-2322); удаления stream resumability (SEP-2575); deprecation Roots, Sampling и Logging (SEP-2577); переклассификации HTTP+SSE (SEP-2596); deprecation Dynamic Client Registration в пользу Client ID Metadata Documents; перенумерации кодов ошибок; и двенадцатимесячного deprecation window. ↩ ↩2 -
Tools,
modelcontextprotocol.io/specification/2026-07-28/server/tools, и Server Features,.../server. Источник таблицы control hierarchy, воспроизведённой выше; формtools/listиtools/call; различияisErrorмежду ошибками протокола и ошибками выполнения инструмента; правил имён инструментов и namespace-примечания с рекомендацией «prefixing tool names with a server identifier»; а также ненормативного руководства «Stateful Tools» по явным handle. ↩ -
Versioning,
modelcontextprotocol.io/specification/versioning. Источник схемыYYYY-MM-DD, состояний ревизий Draft/Current/Final, подтверждения, что 2026-07-28 является current, и правил per-request negotiation. Таблица уровней SDK по адресуmodelcontextprotocol.io/docs/sdkперечисляет TypeScript, Python, C#, Go и Rust как Tier 1, Java и Ruby как Tier 2, а Swift, PHP и Kotlin как Tier 3. ↩ -
A2A Protocol, version 1.0.0,
a2a-protocol.org— спецификация и страница A2A and MCP: Relationship and Distinction, прочитано 7 сентября 2026 года. Источник различия tools-against-agents, утверждения, что два протокола «address distinct but highly complementary needs», и формулировки partnering/using. ↩ -
Agent Communication Protocol,
agentcommunicationprotocol.dev, прочитано 7 сентября 2026 года: «ACP is now part of A2A under the Linux Foundation!», баннер, добавленный над спецификацией, которая всё ещё отдаётся целиком: architecture, agent manifest, agent discovery, message structure, stateful agents, run lifecycle и список REST endpoints всё ещё отвечают 200. Спецификация не исчезла; исчез проект. ↩