Versões do protocolo
Tradução automática
Esta página foi traduzida automaticamente a partir da documentação em inglês, e a página em inglês é a versão de referência. Se algo parecer errado, Traduções explica como avisar.
O MCP tem duas eras.
Os servidores lançados antes de 2026-07-28 abrem toda conexão com o handshake initialize: o cliente propõe uma versão, o servidor responde com outra, o cliente confirma, tudo antes da primeira requisição útil. Os servidores em 2026-07-28 abandonam o handshake. O cliente envia uma única sondagem server/discover e o servidor responde com tudo em um único resultado.
Você quase nunca precisa se preocupar com isso, porque o Client negocia por você. Esta página trata do único argumento do construtor que controla isso, mode=, e das três situações em que você o altera.
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)
Você não passou mode, então recebeu o padrão: "auto". Entrar no async with envia uma única sondagem server/discover na versão mais nova que este SDK fala. Depois:
- Um servidor moderno responde. O cliente adota o resultado. Uma ida e volta, pronto.
- Um servidor mais antigo nunca ouviu falar de
server/discovere retorna um erro. O cliente recorre ao handshake clássicoinitializee fica com o que ele negociar.
De um jeito ou de outro você sai conectado, e client.protocol_version diz qual foi o caso:
2026-07-28
A funcionalidade inteira é essa. Um Client, qualquer era de servidor, sem ramificações no seu código.
Info
O MCPServer responde a server/discover em todos os transportes — em memória, stdio, streamable
HTTP — então, contra o seu próprio servidor, auto sempre chega em 2026-07-28. O fallback só
dispara contra um servidor real anterior a 2026, que é exatamente quando você quer que ele dispare.
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" nunca sonda. Ele executa o handshake initialize, a mesma conexão que um cliente anterior a 2026 abre.
2025-11-25
Mesmo servidor. Ele fala 2026-07-28 perfeitamente bem; você disse ao cliente para não perguntar.
Você quer isso para as funcionalidades no estilo push.
Uma requisição iniciada pelo servidor é o servidor chamando você: ctx.elicit(...) colocando um formulário na frente do seu usuário, a amostragem (sampling) pedindo uma completion ao seu modelo no meio de uma chamada de ferramenta. Esse canal só existe em uma sessão da era do handshake.
Em 2026-07-28 ele não existe mais. O servidor retorna suas perguntas e você repete a chamada com as respostas (Requisições com várias idas e voltas).
mode="auto" só dá um handshake a você quando o servidor é antigo demais para qualquer outra coisa. mode="legacy" garante um. Recorra a ele sempre que passar ao Client(...) um sampling_callback, um elicitation_callback que você quer acionado como requisição, ou um message_handler. Callbacks do cliente passa por cada um deles.
Fixando uma versão
mode também aceita uma string de versão moderna do protocolo. Hoje esse conjunto é exatamente ["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)
Uma versão fixada não envia nada. Sem sondagem, sem handshake. O cliente adota 2026-07-28 localmente e a conexão está ativa no instante em que async with retorna.
Fixar uma versão é uma promessa que você faz: você já sabe que o servidor fala aquela versão. O cliente não verifica.
Check
Fixar uma versão não é uma descoberta. Imprima client.server_info e o preço está bem ali:
None
O cliente nunca perguntou ao servidor quem ele é, então server_info é None. Com client.server_capabilities
é a mesma história: toda capacidade é None. As chamadas de ferramenta continuam funcionando (o protocolo não precisa de nada disso);
o código que lê server_capabilities para decidir o que oferecer, não.
A próxima seção é a correção.
Só as versões modernas podem ser fixadas. Uma string da era do handshake é rejeitada na construção, antes de qualquer I/O, e o erro diz o que escrever no lugar:
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')
Reconectando com prior_discover
A sondagem é barata, mas ainda é uma ida e volta que você paga a cada reconexão, e a resposta quase nunca muda.
Então guarde-a. Depois de uma conexão auto, client.session.discover_result contém o DiscoverResult exato que o servidor enviou: seu supported_versions, seu capabilities, seu instructions e a identidade que o servidor carimbou no _meta do resultado. Passe-o de volta como prior_discover= na próxima vez:
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
A segunda conexão fez zero idas e voltas de negociação e ainda sabe exatamente com quem está falando. Esse é o modo fixado feito direito: mode= nomeia a versão, prior_discover= fornece a identidade. ✨
DiscoverResult é um modelo Pydantic. saved.model_dump_json() vai para um arquivo ou um cache; DiscoverResult.model_validate_json(...) o traz de volta no próximo processo.
Tip
prior_discover= só faz alguma coisa quando mode é uma versão fixada. Com "auto" o cliente
sonda o servidor de qualquer forma, e com "legacy" ele é ignorado.
Os quatro modos
| Você escreve | Tráfego de negociação | Você recebe |
|---|---|---|
Client(target) |
uma sondagem server/discover; o handshake initialize se ela falhar |
a versão mais nova que os dois lados falam, de qualquer era |
Client(target, mode="legacy") |
o handshake initialize |
uma versão da era do handshake; requisições iniciadas pelo servidor funcionam |
Client(target, mode="2026-07-28") |
nenhum | aquela versão, fixada, com server_info como None |
Client(target, mode="2026-07-28", prior_discover=saved) |
nenhum | aquela versão, fixada, e a identidade que você salvou da última vez |
Recapitulando
- O MCP tem uma era do handshake (até
2025-11-25, o handshakeinitialize) e uma era moderna (2026-07-28,server/discover). OClientfaz a ponte entre elas. mode="auto"é o padrão: sondar, recorrer ao fallback. Deixe como está, a menos que uma das outras três linhas descreva o seu caso.client.protocol_versioné sempre a resposta para "o que eu recebi?".mode="legacy"força o handshake. É disso que você precisa para requisições iniciadas pelo servidor: amostragem, elicitação (elicitation) via push,message_handler.- Uma versão fixada (
mode="2026-07-28") não envia nenhum tráfego de negociação, ao custo declient.server_infoserNone. prior_discover=paga esse custo de volta: salveclient.session.discover_result, reconecte com ele, fique com os dois.
Uma conexão moderna não tem canal de push, então como um servidor de 2026 faz uma pergunta a você no meio de uma chamada? Ele a retorna: Requisições com várias idas e voltas.