Версії протоколу
Машинний переклад
Цю сторінку перекладено автоматично з англомовної документації, і основною версією є англомовна сторінка. Якщо щось читається неправильно, на сторінці Переклади пояснено, як про це повідомити.
У MCP є два покоління.
Сервери, випущені до 2026-07-28, відкривають кожне з'єднання рукостисканням initialize: клієнт пропонує версію, сервер відповідає своєю, клієнт підтверджує — і все це до першого корисного запиту. Сервери версії 2026-07-28 обходяться без рукостискання. Клієнт надсилає один пробний запит server/discover, а сервер відповідає на нього всім одразу в єдиному результаті.
Перейматися цим майже ніколи не доводиться, бо Client домовляється за вас. Ця сторінка — про єдиний аргумент конструктора, який цим керує, mode=, і про три випадки, коли його змінюють.
mode="auto"
from mcp import Client
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r}."
async def main() -> None:
async with Client(mcp) as client:
print(client.protocol_version)
mode не передано, тож діє значення за замовчуванням: "auto". Вхід в async with надсилає один пробний запит server/discover найновішої версії, якою володіє цей SDK. Далі:
- Сучасний сервер відповідає на нього. Клієнт приймає результат. Один раунд обміну — і готово.
- Старіший сервер ніколи не чув про
server/discoverі повертає помилку. Клієнт переходить до класичного рукостисканняinitializeі бере те, про що воно домовиться.
Так чи так, з'єднання встановлено, а client.protocol_version підкаже, який варіант спрацював:
2026-07-28
Оце й уся можливість. Один Client, сервер будь-якого покоління, жодних розгалужень у вашому коді.
Info
MCPServer відповідає на server/discover на кожному транспорті — in-memory, stdio, streamable
HTTP — тож із власним сервером auto завжди зупиняється на 2026-07-28. Запасний шлях
спрацьовує лише проти справжнього сервера, випущеного до 2026, — саме тоді, коли він і потрібен.
mode="legacy"
from mcp import Client
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r}."
async def main() -> None:
async with Client(mcp, mode="legacy") as client:
print(client.protocol_version)
mode="legacy" ніколи не надсилає пробного запиту. Він виконує рукостискання initialize — таке саме з'єднання, яке відкриває клієнт, випущений до 2026.
2025-11-25
Той самий сервер. Він чудово говорить 2026-07-28; це ви сказали клієнту не питати.
Це потрібно для можливостей у push-стилі.
Запит, ініційований сервером, — це коли сервер викликає вас: ctx.elicit(...) показує форму вашому користувачу, семплювання (sampling) просить вашу модель про завершення посеред виклику інструмента. Цей канал існує лише в сесії покоління з рукостисканням.
У 2026-07-28 його вже немає. Сервер повертає свої запитання, а ви повторюєте виклик із відповідями (Багатораундові запити (multi-round-trip)).
mode="auto" дає рукостискання лише тоді, коли сервер застарий для чогось іншого. mode="legacy" його гарантує. Беріться за нього щоразу, коли передаєте в Client(...) параметр sampling_callback, elicitation_callback, який має оброблятися як запит, або message_handler. Кожен із них розібрано на сторінці Колбеки клієнта.
Фіксація версії
mode також приймає рядок сучасної версії протоколу. Сьогодні ця множина — рівно ["2026-07-28"].
from mcp import Client
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r}."
async def main() -> None:
async with Client(mcp, mode="2026-07-28") as client:
print(client.protocol_version)
Фіксація не надсилає нічого. Ні пробного запиту, ні рукостискання. Клієнт локально приймає 2026-07-28, і з'єднання готове тієї ж миті, коли завершується вхід в async with.
Фіксація — це обіцянка, яку даєте ви: ви вже знаєте, що сервер говорить цією версією. Клієнт не перевіряє.
Check
Фіксація — не виявлення. Виведіть client.server_info — і ціну одразу видно:
None
Клієнт ніколи не питав сервер, хто він, тож server_info дорівнює None. З client.server_capabilities
та сама історія: кожна можливість — None. Виклики інструментів усе ще працюють (протоколу нічого з цього не потрібно);
код, який читає server_capabilities, щоб вирішити, що пропонувати, — ні.
Наступний розділ це виправляє.
Фіксувати можна лише сучасні версії. Рядок покоління з рукостисканням відхиляється ще під час створення об'єкта, до будь-якого вводу-виводу, а помилка підказує, що написати натомість:
ValueError: mode must be 'legacy', 'auto', or one of ['2026-07-28']; got '2025-06-18' ('2025-06-18' is a handshake-era version; use mode='legacy')
Повторне під'єднання з prior_discover
Пробний запит дешевий, але це все одно раунд обміну, за який доводиться платити при кожному повторному під'єднанні, а відповідь майже ніколи не змінюється.
Тож збережіть її. Після з'єднання в режимі auto client.session.discover_result містить точний DiscoverResult, який надіслав сервер: його supported_versions, capabilities, instructions і відомості про себе, які сервер записав у _meta результату. Наступного разу передайте його назад як prior_discover=:
from mcp import Client
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r}."
async def main() -> None:
async with Client(mcp) as client:
saved = client.session.discover_result
async with Client(mcp, mode="2026-07-28", prior_discover=saved) as client:
print(client.protocol_version)
if client.server_info is not None:
print(client.server_info.name)
2026-07-28
Bookshop
Друге з'єднання зробило нуль раундів узгодження й усе одно точно знає, з ким розмовляє. Оце і є зафіксований режим, зроблений як слід: mode= називає версію, prior_discover= надає відомості про сервер. ✨
DiscoverResult — модель Pydantic. saved.model_dump_json() іде у файл або кеш; DiscoverResult.model_validate_json(...) відновлює його в наступному процесі.
Tip
prior_discover= щось робить лише тоді, коли mode — це зафіксована версія. За "auto" клієнт
усе одно надсилає серверу пробний запит, а за "legacy" параметр ігнорується.
Чотири режими
| Що пишете | Трафік узгодження | Що отримуєте |
|---|---|---|
Client(target) |
один пробний запит server/discover; рукостискання initialize, якщо він не вдався |
найновіша версія, якою володіють обидві сторони, будь-якого покоління |
Client(target, mode="legacy") |
рукостискання initialize |
версія покоління з рукостисканням; запити, ініційовані сервером, працюють |
Client(target, mode="2026-07-28") |
немає | ця версія, зафіксована, а server_info дорівнює None |
Client(target, mode="2026-07-28", prior_discover=saved) |
немає | ця версія, зафіксована, і відомості про сервер, збережені минулого разу |
Підсумки
- У MCP є покоління з рукостисканням (до
2025-11-25включно, рукостисканняinitialize) і сучасне покоління (2026-07-28,server/discover).Clientз'єднує їх. mode="auto"— значення за замовчуванням: пробний запит, потім запасний шлях. Не чіпайте його, якщо жоден з інших трьох рядків не про вас.client.protocol_version— завжди відповідь на питання «що я отримав?».mode="legacy"примусово вмикає рукостискання. Саме це потрібно для запитів, ініційованих сервером: семплювання, push-еліцитація (elicitation),message_handler.- Фіксація версії (
mode="2026-07-28") взагалі не надсилає трафіку узгодження — ціною того, щоclient.server_infoдорівнюєNone. prior_discover=повертає цю ціну: збережітьclient.session.discover_result, під'єднайтеся з ним знову — і отримаєте обидва.
Сучасне з'єднання не має push-каналу, то як сервер 2026 ставить вам запитання посеред виклику? Він його повертає: Багатораундові запити.