Начало / Протокол / Версии и совместимость
Версии и совместимость
Как клиент и сервер договариваются, на каком протоколе говорят
Здесь описано, как клиент и сервер согласуют протокол: версию, объявляемую в каждом запросе, необязательные расширения и совместимость с прежними версиями, где было рукопожатие. Согласования-рукопожатия больше нет. Каждый запрос несёт свою версию, а сервер принимает или отклоняет каждый запрос независимо.
Термины#
- Современные версии передают версию, идентичность и возможности как метаданные каждого запроса (ревизия 2026-07-28 и новее).
- Прежние версии устанавливают сессию рукопожатием
initialize(2025-11-25 и ранее). - Двухэпохная реализация поддерживает и современные, и прежние версии.
Согласование версии#
Каждый запрос объявляет версию в _meta, а по HTTP ещё и в заголовке MCP-Protocol-Version (подробнее). Если сервер не реализует запрошенную версию (неизвестную ему или известную, но отключённую), он ДОЛЖЕН ответить ошибкой UnsupportedProtocolVersionError со списком поддерживаемых версий:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32022,
"message": "Unsupported protocol version",
"data": {
"supported": ["2026-07-28", "2025-11-25"],
"requested": "1900-01-01"
}
}
}Клиенту СЛЕДУЕТ выбрать из supported общую версию и повторить запрос либо показать пользователю ошибку, если совместимой версии нет.
Серверы ДОЛЖНЫ реализовывать server/discover. Клиенты МОГУТ вызвать его до других запросов, чтобы заранее узнать версии, но не обязаны: можно сразу вызвать любой метод и обработать UnsupportedProtocolVersionError.
Согласование расширений#
Клиенты и серверы могут договариваться о необязательных расширениях. Расширения объявляются в поле extensions возможностей. Это словарь «идентификатор расширения → настройки». Идентификатор ДОЛЖЕН подчиняться правилам именования ключей _meta с обязательным префиксом.
Пример клиента, который объявляет расширение MCP Apps (io.modelcontextprotocol/ui):
{
"capabilities": {
"roots": {},
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
}
}Пример расширения Tasks (io.modelcontextprotocol/tasks):
{
"capabilities": {
"tools": {},
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
}
}Каждое расширение определяет схему своих настроек, а пустой объект значит поддержку без дополнительных настроек. Если одна сторона поддерживает расширение, а другая нет, поддерживающая сторона ДОЛЖНА либо вернуться к поведению базового протокола, либо отклонить запрос подходящей ошибкой. Расширениям СЛЕДУЕТ описывать, как они ведут себя в таком случае.
Совместимость с версиями на основе рукопожатия#
Сервер, который хочет поддерживать и прежних клиентов (ждущих initialize), и современных (с метаданными в запросах), МОЖЕТ реализовать оба поведения. Клиент, которому нужно работать с серверами обоих видов, определяет эпоху сервера способом, зависящим от транспорта.
- stdio. Отправить пробный
server/discoverи откатиться, если пришла любая ошибка, не распознанная как современная. - Streamable HTTP. Отправить современный запрос и при
400 Bad Requestпроверить тело, прежде чем откатываться (подробнее).
В обоих случаях распознанная современная ошибка JSON-RPC (например, UnsupportedProtocolVersionError) означает современный сервер: нужно повторить запрос с поддерживаемой версией, а не откатываться. Всё остальное означает прежний сервер.
Эпоха — свойство сервера, а не отдельного запроса. Клиентам СЛЕДУЕТ кэшировать результат на время жизни процесса сервера (stdio) или источника (HTTP) и МОГУТ сохранять его между перезапусками той же конфигурации, повторяя пробу, если предположение перестало работать.
Сервер, который поддерживает только современные версии, СЛЕДУЕТ называть поддерживаемые версии в любой ошибке на запрос initialize, на любом транспорте: у прежних клиентов нет способа перейти вперёд, и это сообщение может оказаться единственным, что они смогут показать пользователю.
Матрица совместимости#
Результат для каждого сочетания клиента и сервера. Нажмите на ячейку, чтобы прочитать подробности.
сервер →
работаетне работаетв таблице спецификации не указано
Та же матрица в виде таблицы:
| Клиент | Сервер | Итог |
|---|---|---|
| Современный | Современный | Работает. server/discover необязателен; несовпадение версий возвращается как UnsupportedProtocolVersionError, и клиент повторяет запрос с общей версией. |
| Современный | Прежний | Не работает. Сервер может отклонить запрос по-своему, промолчать или даже обработать неоднозначный по эпохе метод по прежним правилам. На stdio клиентам СЛЕДУЕТ отправить server/discover первым, чтобы отказ был предсказуемым. Затем клиент показывает пользователю понятную ошибку. |
| Двухэпохный | Современный | Работает. Проба на stdio возвращает DiscoverResult (или UnsupportedProtocolVersionError); по HTTP первый современный запрос проходит или возвращает современную ошибку. Клиент остаётся современным. |
| Двухэпохный | Прежний | Работает. stdio: проба возвращает не-современную ошибку или молчит, клиент возвращается к initialize. HTTP: современный запрос получает 4xx без распознанной современной ошибки, клиент возвращается к initialize (и, возможно, дальше к устаревшему HTTP+SSE). |
| Прежний | Современный | Не работает. stdio: сервер отвечает на initialize ошибкой JSON-RPC, код зависит от реализации. HTTP: запрос без обязательных заголовков отклоняется статусом 400 Bad Request (клиент на HTTP+SSE споткнётся уже на начальном GET). У прежних клиентов нет способа перейти вперёд. |
| Прежний | Двухэпохный | Работает. Сервер отвечает на initialize и обслуживает клиента по согласованной прежней ревизии. |
| Прежний | Прежний | Работает по правилам прежней ревизии; вне рамок этого документа. |
Двухэпохный сервер выбирает поведение по тому, как клиент начинает обмен. Запрос с современными _meta обслуживается без состояния по этой ревизии. Запрос initialize выбирает прежние правила, ограниченные процессом stdio или сессией HTTP по согласованной прежней версии. Такой сервер МОЖЕТ обслуживать обе эпохи одновременно на одной конечной точке или в одном процессе.