O que há de novo na v2
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.
Duas coisas aconteceram ao mesmo tempo na v2. O SDK foi reconstruído: um motor novo por baixo tanto do cliente quanto do servidor, um Client de primeira classe e um conjunto de renomeações em que uma base de código v1 esbarra logo no primeiro import. E o protocolo mudou: a v2 fala a revisão 2026-07-28 do MCP, que remove o handshake de conexão, a sessão e toda requisição iniciada pelo servidor, sem abandonar os clientes que você já tem.
Esta página é o tour pelas duas metades, uma seção por destaque, cada uma terminando na página responsável pelo assunto. Não é o manual de como portar. Esse é o Guia de migração: cada quebra de compatibilidade, com o código de antes e de depois.
A v2 é a linha estável
pip install mcp instala a 2.x, e Instalação tem a linha de
instalação para copiar e colar. Se algo na v2 quebrar, surpreender ou atrasar você,
conte para nós.
O SDK: da v1 para a v2
FastMCP agora é MCPServer
A classe de servidor de alto nível foi renomeada, e o módulo dela junto. É a primeira coisa em que todo servidor v1 esbarra, porque o caminho de import antigo sumiu em vez de ficar obsoleto:
from mcp.server import MCPServer # v1: from mcp.server.fastmcp import FastMCP
mcp = MCPServer("Demo") # v1: FastMCP("Demo")
Para um servidor feito com decoradores, isso também é a maior parte do trabalho de portar. @mcp.tool(), @mcp.resource() e @mcp.prompt() aceitam o que aceitavam na v1 (@mcp.resource() ganha um argumento nomeado opcional, security=), e o schema de entrada continua vindo das suas anotações de tipo. Em volta disso: tudo que ficava em mcp.server.fastmcp.* agora vive em mcp.server.mcpserver.*, ctx.fastmcp virou ctx.mcp_server, get_context() sumiu (declare um parâmetro ctx: Context no lugar), e a exceção base FastMCPError virou MCPServerError. O Guia de migração tem a tabela de imports.
Resolve: o novo jeito de pedir informações ao usuário
Nem tudo de que uma ferramenta (tool) precisa deve vir do modelo. Novidade na v2: um parâmetro de ferramenta anotado com Resolve(fn) é preenchido por uma função que você escreve, de forma invisível para o modelo, e essa função pode retornar Elicit(...) para apresentar uma pergunta ao usuário. Esse é o jeito preferido de obter qualquer coisa do cliente no meio de uma chamada: o SDK leva a pergunta pelo mecanismo que a conexão suportar (uma requisição de elicitação (elicitation) ao vivo para um cliente legado, um multi-round-trip na 2026-07-28), então um único corpo de ferramenta atende as duas eras. Dependências é a página.
Note
As outras duas formas continuam lá para quando você precisar delas: ctx.elicit() ainda
funciona para clientes em conexões legadas (Elicitação), e um
handler pode retornar ele mesmo um InputRequiredResult e conduzir as rodadas à mão, que é
também como as requisições de amostragem (sampling) e de roots trafegam na 2026-07-28
(Requisições multi-round-trip).
Um Client de primeira classe
A v1 entregava três camadas aninhadas: um gerenciador de contexto de transporte que produzia streams brutos, uma ClientSession em volta deles e um await session.initialize() chamado à mão. A v2 tem um objeto só:
from mcp import Client
from mcp.server import MCPServer
mcp = MCPServer("Bookshop", instructions="Search the catalog before recommending a book.")
@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.server_info)
print(client.server_capabilities)
print(client.protocol_version)
print(client.instructions)
Client recebe um objeto de servidor (em memória, sem transporte: é o cenário dos testes), uma URL (Streamable HTTP) ou qualquer gerenciador de contexto de transporte, como stdio_client(...). Entrar no async with conecta e negocia a versão do protocolo, seja qual for a era que o servidor fale; client.server_capabilities e client.protocol_version simplesmente estão lá depois disso, e client.server_info também, quando o servidor se identifica (agora ele é Implementation | None, já que na era 2026 a identidade é opcional). Os callbacks de amostragem e de elicitação que você registrou na v1 continuam funcionando (o corpo deles passa pela mesma renomeação de atributos para snake_case que todo o resto desta página), agora também respondem às requisições-dentro-de-resultados no estilo 2026 (abaixo), e rodam de forma concorrente em vez de um por vez. ClientSession continua por baixo para quem quer a superfície de baixo nível, e client.session a entrega para você; ela também mudou (roda sobre o novo motor de dispatcher, e algumas das próprias assinaturas dela mudaram), então leia o Guia de migração antes de descer de nível.
O Client o apresenta, Transportes do cliente cobre as três formas de conexão, Callbacks do cliente cobre os callbacks em si, e Testes mostra o padrão em memória que substitui o helper create_connected_server_and_client_session() da v1.
O Server de baixo nível foi reconstruído, não renomeado
Se você trabalha na camada JSON-RPC, esta é a parte "tudo é diferente" da v2. Aqui está o mesmo servidor de uma ferramenta só das duas formas; clique nos marcadores para ver o que mudou de lugar.
from typing import Any
import mcp.types as types
from mcp.server.lowlevel import Server
server = Server("Bookshop")
@server.list_tools() # (1)!
async def list_tools() -> list[types.Tool]:
return [ # (2)!
types.Tool(
name="search_books",
description="Search the catalog by title or author.",
inputSchema={ # (3)!
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict[str, Any]) -> list[types.ContentBlock]: # (4)!
if name != "search_books":
raise ValueError(f"Unknown tool: {name}") # (5)!
ctx = server.request_context # (6)!
return [types.TextContent(type="text", text=f"Found 3 books matching {arguments['query']!r}.")] # (7)!
- Os handlers são registrados com decoradores (chamados, com parênteses), a qualquer momento depois que o servidor existe.
- Você retorna uma
list[Tool]pura e o SDK a embrulha em umListToolsResult. - Os campos são camelCase em Python, e o schema é aplicado: o SDK valida os argumentos de
call_toolcontra ele com jsonschema antes de a sua função rodar, e é por isso quearguments["query"]abaixo é seguro. - Um único handler
call_toolatende todas as ferramentas, e recebe o nome da ferramenta e os argumentos já validados, desempacotados e nuncaNone. - Lançar uma exceção é como uma ferramenta v1 sinaliza falha: qualquer exceção é capturada e retornada como
CallToolResult(isError=True)comstr(e)como texto, então o modelo que fez a chamada lê essa mensagem e pode tentar de novo. - O contexto vem de uma ContextVar ambiente, alcançada pelo objeto do servidor no meio da requisição.
- Blocos de conteúdo puros são embrulhados em um
CallToolResultpara você.
from mcp import MCPError
from mcp.server import Server, ServerRequestContext
from mcp.types import (
INVALID_PARAMS,
CallToolRequestParams,
CallToolResult,
ListToolsResult,
PaginatedRequestParams,
TextContent,
Tool,
)
SEARCH_BOOKS = Tool(
name="search_books",
description="Search the catalog by title or author.",
input_schema={ # (1)!
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
)
async def list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult: # (2)!
return ListToolsResult(tools=[SEARCH_BOOKS]) # (3)!
async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult: # (4)!
if params.name != "search_books":
raise MCPError(INVALID_PARAMS, f"Unknown tool: {params.name}") # (5)!
args = params.arguments or {} # (6)!
text = f"Found 3 books matching {args['query']!r}."
return CallToolResult(content=[TextContent(type="text", text=text)]) # (7)!
server = Server("Bookshop", on_list_tools=list_tools, on_call_tool=call_tool) # (8)!
- Os campos agora são snake_case, e o schema é anunciado, mas nunca aplicado: nada confere os argumentos antes de o seu handler rodar.
- Todo handler tem o mesmo formato:
async (ctx, params) -> result. O contexto é o primeiro argumento (ctx.session,ctx.request_id,ctx.protocol_versionmoram nele); é aqui queserver.request_contextfoi parar. - Você monta o
ListToolsResultcompleto por conta própria. Retornar uma lista pura agora é umTypeErrorno lado do servidor, não algo que o SDK embrulha. - Entram params tipados (
params.name,params.arguments), sai um resultado completo. Nada é desempacotado, embrulhado ou convertido para você. - A mesma verificação, outro verbo. Um
ValueErroraqui chegaria ao modelo como um-32603opaco (veja abaixo), então um erro de protocolo deliberado é lançado comoMCPError: ele passa direto, com código e mensagem intactos, e-32602com esse texto é a resposta da própria especificação para uma ferramenta desconhecida. params.argumentspode serNone; a v1 o trocava por{}antes mesmo de o seu código vê-lo. Sem validação na frente do handler, esta linha é indispensável.- Uma exceção inesperada lançada aqui vira um erro de protocolo sanitizado,
-32603"Internal server error": o modelo nunca vê a mensagem. Para uma falha que o modelo deva ler e à qual deva reagir, retorneCallToolResult(is_error=True, ...). - Os handlers são argumentos do construtor, então a superfície do servidor está completa no instante em que ele existe;
add_request_handler()é a saída de emergência pós-construção, e a porta para métodos personalizados.
O exemplo é o padrão. De forma mais geral: todo handler tem o mesmo formato, com params tipados na entrada e um tipo de resultado completo na saída; a antiga verificação com jsonschema dos argumentos de ferramenta sumiu; uma exceção é um erro de protocolo, nunca um resultado de ferramenta com is_error=True; e a ContextVar ambiente server.request_context sumiu. Métodos personalizados, com namespace de fornecedor, são de primeira classe via add_request_handler(method, params_type, handler), que valida os params de entrada contra o seu modelo antes de o seu handler rodar. E uma lista middleware (marcada como provisória de propósito) envolve toda mensagem de entrada, substituindo os métodos privados _handle_* que as pessoas costumavam sobrescrever.
Por baixo dos panos, o loop de recebimento do BaseSession da v1 foi substituído por um motor de dispatcher que cliente e servidor agora compartilham, e é ele que torna várias coisas desta página verdadeiras ao mesmo tempo: um único objeto Server atende as duas eras do protocolo, Client(server) despacha dentro do processo sem o enquadramento JSON-RPC, e uma requisição de cliente que estoura o timeout agora cancela de fato o handler do lado do servidor.
O Server de baixo nível é a página; o Guia de migração percorre cada hook removido. Se você nunca desceu abaixo do MCPServer, nada disso afeta você.
Os tipos do protocolo foram para mcp-types, e todo campo é snake_case
Os tipos do protocolo agora vivem em uma distribuição própria, mcp-types. Ela não depende de nada além de pydantic e typing-extensions, então um gateway, um proxy ou um gerador de código consegue consumir os formatos de mensagem do MCP sem instalar uma pilha HTTP: um projeto assim instala mcp-types e importa mcp_types. O próprio mcp depende desse pacote em uma versão exata e o reexpõe, então o código que depende do SDK continua escrevendo import mcp.types as types e from mcp.types import Tool (um alias permanente, cada nome é o mesmo objeto) e declara apenas a sua única dependência real, mcp. A regra prática: importe pelo pacote do qual você de fato depende.
Nesses tipos, todo atributo Python agora é snake_case: result.is_error, tool.input_schema, listing.next_cursor. O JSON que trafega é camelCase, exatamente como antes; só a grafia dos atributos mudou. Dois padrões mais rígidos vêm junto: campos desconhecidos são ignorados em vez de preservados na ida e volta (coloque os extras em _meta), e os dois lados validam o tráfego contra a versão do protocolo que negociaram. Veja o Guia de migração para a tabela de renomeações.
A configuração de transporte foi para run()
MCPServer(...) diz respeito ao que o seu servidor é: o nome, as instruções, o lifespan, a autenticação. Como ele é servido agora é assunto de run() e dos construtores de app, e foi para lá que host, port, stateless_http, json_response, os caminhos dos endpoints e transport_security foram (MCPServer("x", port=9000) é um TypeError). As sobrecargas são tipadas por transporte, então o seu editor diz quais opções stdio aceita e quais streamable-http aceita. Uma remoção que vale conhecer: mount_path sumiu; montar o app ASGI é o jeito suportado de servir sob um prefixo.
Executando seu servidor cobre as opções; Adicionar a um app existente cobre a montagem.
Comportamento que muda sem erro de import
As renomeações se anunciam sozinhas. Estas aqui, não:
- Funções síncronas rodam em uma thread de trabalho. Uma ferramenta
def(ou recurso, prompt ou resolvedor) não bloqueia mais o loop de eventos; a contrapartida é que o corpo dela não roda mais na thread do loop de eventos, o que importa para código com afinidade de thread. Handlersasync defficam intocados. Guia de migração. MCPError(oMcpErrorda v1) lançado dentro de uma ferramenta agora é um erro de protocolo. O modelo nunca o vê. Toda outra exceção continua virando um resultadois_error=Trueque o modelo pode ler e ao qual pode reagir. Tratando erros explica a divisão.- Os resultados são validados antes de sair. Uma
Toolmontada à mão cujoinput_schemaé{}agora falha emtools/list(a especificação exige"type": "object"). Servidores construídos com@mcp.tool()nunca veem isso; o SDK escreve os schemas deles. - O seu cliente valida o que recebe.
list_tools()ecall_tool()conferem a resposta do servidor contra a versão de protocolo negociada, então um servidor quase válido que o parsing tolerante da v1 aceitava agora lançapydantic.ValidationError. Se você se conecta a servidores que não controla, espere ser você quem os descobre; o Guia de migração tem os detalhes. - Templates de URI agora são RFC 6570 de verdade.
{+path},{?query}e companhia funcionam, a correspondência é exata em vez de frouxa à base de regex, e path traversal nos valores extraídos é rejeitado por padrão. Templates mais rígidos falham no momento da decoração, não na primeira requisição. Templates de URI. - O lifespan do Streamable HTTP roda uma vez só, na inicialização, e o estado dele é compartilhado por toda sessão e requisição. Na v1 ele rodava uma vez por sessão, e uma vez por requisição com
stateless_http=True. Pools e caches montados em um lifespan ficam drasticamente mais baratos; qualquer coisa que adquiria ali um recurso por conexão agora pertence ao corpo do handler. Lifespan. mcp devemcp installfixam o ambiente que criam na versão do SDK que você tem instalada. Os dois comandos rodam o seu servidor em um ambienteuv run --with ...novo, que antes resolviamcppara a versão estável mais recente em vez da versão contra a qual você está desenvolvendo. Guia de migração.- O cliente HTTP agora é
httpx2, nãohttpx. A troca de dependência muda o que o seu código captura e repassa (httpx2.AsyncClient,httpx2.ConnectError), e muda como os certificados TLS são verificados:httpx2valida viatruststorecontra o repositório de certificados confiáveis do sistema operacional em vez da lista de CAs embutida do certifi. A maioria dos ambientes nem percebe; um contêiner mínimo sem repositório de CAs do sistema, ou uma CA privada que só o bundle do certifi conhecia, começa a falhar no handshake TLS. DefinaSSL_CERT_FILE/SSL_CERT_DIRou passeverify=ssl_contextpara o seu cliente. Guia de migração.
Removidos de vez
Cada um destes é uma seção no Guia de migração:
- O transporte WebSocket, dos dois lados, e o extra
mcp[ws]. Nunca fez parte da especificação do MCP. - A API experimental de Tasks (
mcp.*.experimental). A 2026-07-28 tira as tasks do núcleo do protocolo e as leva para uma extensão oficial (SEP-2663), que este SDK ainda não implementa. mcp.shared.version,mcp.shared.progressemcp.shared.session(junto com o stubRequestResponderque as anotações demessage_handlerda v1 importavam) como caminhos de import. (mcp.typesnão foi removido: continua como alias permanente do pacote independentemcp_types.)- A grafia obsoleta
streamablehttp_client, e o callbackget_session_iddestreamable_http_client(que agora produz exatamente dois streams). McpError, renomeado paraMCPErrorcom um construtor direto(code, message, data).MCPServer.get_context(),mount_path=, e os métodos decoradores, a ContextVar e os dicts de handlers doServerde baixo nível.
O protocolo: de 2025-11-25 para 2026-07-28
A v2 implementa a revisão 2026-07-28 e serve as duas revisões ao mesmo tempo: o mesmo streamable_http_app() (e o mesmo servidor stdio) responde ao initialize de um cliente da era 2025 e às requisições de um cliente da era 2026 sem nada para configurar, sem flag para virar e sem deploy separado. Servir a revisão nova não abandona um cliente que está na antiga. O que vem a seguir é o que a revisão nova em si muda.
Sem handshake, sem sessão
Um cliente 2026-07-28 não abre uma conexão, negocia e só então conversa. Toda requisição carrega a versão do protocolo, as informações do cliente e as capacidades do cliente em _meta, e a única chamada de descoberta, server/discover, é uma requisição comum como qualquer outra. Client faz a coisa certa por padrão: sonda server/discover uma vez e recua para o handshake initialize se o servidor for mais antigo.
Sobre Streamable HTTP não existe Mcp-Session-Id no caminho 2026, e esse é o grande destaque operacional: nada amarra uma requisição moderna a um worker, então qualquer réplica atrás de um balanceador de carga round-robin simples pode respondê-la. Duas ressalvas honestas. Os seus clientes da era 2025 (hoje, isso é a maioria dos clientes) ainda abrem sessões e ainda precisam de toda a afinidade de sessão de que precisavam na v1; nada muda para eles. E a única coisa que uma nova tentativa multi-round-trip precisa carregar entre workers é o seu request_state selado, cuja chave padrão é gerada por processo, então um deploy com escala horizontal passa RequestStateSecurity(keys=[...]). (stateless_http=True não tem relação: ele só afeta como os clientes da era 2025 são servidos, e o tráfego 2026 nunca o lê; se você já o definia na v1, nada muda.)
Versões do protocolo é o lado do cliente disso, Deploy e escala é o checklist do operador (a allowlist de Host, a chave do request_state, notificações entre réplicas), e Servindo clientes legados é a história das duas eras ao mesmo tempo.
O servidor não pode chamar o cliente: requisições multi-round-trip
Toda requisição iniciada pelo servidor sumiu na 2026-07-28: elicitação por push, amostragem, roots/list. Em uma conexão 2026 não há canal para elas, então ctx.elicit() e ctx.session.create_message() falham ali com NoBackChannelError (continuam funcionando para clientes legados).
A substituição inverte a chamada. Uma ferramenta que precisa de algo do usuário retorna a pergunta (InputRequiredResult), o cliente a responde com os mesmos callbacks que sempre teve, e a chamada é repetida com as respostas anexadas. Client conduz esse loop para você. No servidor você raramente monta o resultado por conta própria, porque uma dependência faz isso: anote um parâmetro com Resolve(ask_quantity), onde ask_quantity é uma função comum que você escreve, e o SDK pergunta pelo mecanismo que a conexão suportar, uma requisição de elicitação ao vivo em uma sessão legada ou um multi-round-trip na 2026. Um corpo de ferramenta, as duas eras:
from typing import Annotated
from pydantic import BaseModel
from mcp import Client
from mcp.client import ClientRequestContext
from mcp.server import MCPServer
from mcp.server.mcpserver import AcceptedElicitation, Elicit, ElicitationResult, Resolve
from mcp.types import ElicitRequestParams, ElicitResult
mcp = MCPServer("Bookshop")
class Quantity(BaseModel):
copies: int
async def ask_quantity() -> Elicit[Quantity]:
"""Resolver: ask the user how many copies to put aside."""
return Elicit("How many copies?", Quantity)
@mcp.tool()
async def reserve(title: str, quantity: Annotated[ElicitationResult[Quantity], Resolve(ask_quantity)]) -> str:
"""Reserve copies of a book, asking the user how many."""
if isinstance(quantity, AcceptedElicitation):
return f"Reserved {quantity.data.copies} of {title!r}."
return "Nothing reserved."
async def answer(context: ClientRequestContext, params: ElicitRequestParams) -> ElicitResult:
return ElicitResult(action="accept", content={"copies": 2})
async def main() -> None:
async with (
Client(mcp, mode="legacy", elicitation_callback=answer) as legacy,
Client(mcp, elicitation_callback=answer) as modern,
):
for client in (legacy, modern):
result = await client.call_tool("reserve", {"title": "Dune"})
print(client.protocol_version, result.structured_content)
Esse arquivo é a proposta inteira em um lugar só: um servidor, uma ferramenta apoiada em Resolve, e um cliente legado mais um cliente moderno, os dois recebendo a sua resposta, em memória. Requisições multi-round-trip explica o mecanismo (incluindo o request_state, que o SDK sela e verifica para você); Elicitação cobre a parte de perguntar.
Este é o único lugar em que um servidor v1 portado muda de comportamento
Os seus próprios testes esbarram nisso primeiro: Client(mcp) negocia 2026-07-28 com o seu
servidor v2 por padrão, então uma ferramenta que chama ctx.elicit() falha em um teste que
passava na v1. Mova a pergunta para um parâmetro Resolve(...) (portável entre eras), ou fixe o
cliente de teste em mode="legacy" se você quer mesmo o comportamento de push.
Roots, amostragem e logging de protocolo estão obsoletos; ping foi removido
A SEP-2577 marca como obsoletas três capacidades inteiras, em toda versão do protocolo: roots, amostragem e logging no nível do MCP (ctx.info() e companhia). Esse é um eixo separado do canal de retorno (back-channel) ausente acima; obsoleto é só um aviso, tudo continua funcionando em sessões da era 2025, e nada muda no que trafega. O que você nota é o MCPDeprecationWarning, que é um UserWarning, então ele aparece por padrão; espere que o seu primeiro ctx.info(...) depois da atualização avise isso.
ping é mais severo: removido do protocolo, não obsoleto. Dois dos métodos avulsos das funcionalidades obsoletas são removidos na 2026-07-28 do mesmo jeito, logging/setLevel e o notifications/roots/list_changed do cliente, e as notificações de progresso agora vão apenas do servidor para o cliente.
Funcionalidades obsoletas tem a tabela completa, o substituto de cada uma, e o filtro de uma linha caso você precise de um log silencioso enquanto serve clientes legados.
Notificações de mudança viram um stream só
Na 2026-07-28, o stream HTTP GET avulso e resources/subscribe são substituídos por subscriptions/listen: o cliente abre um stream de longa duração e informa os tipos de notificação que quer. O MCPServer o serve por padrão; você publica com await ctx.notify_resource_updated(uri) (e notify_tools_changed(), e assim por diante), um middleware pode recusar uma requisição de listen por chamador, e deploys com várias réplicas encaixam um SubscriptionBus compartilhado. No cliente, async with client.listen(...) abre o stream: o filtro entra como argumentos nomeados, eventos de mudança tipados voltam, e sub.honored é o subconjunto que o servidor concordou em entregar.
Assinaturas cobre publicar e servir, a página gêmea em Clientes a ponta que observa, e Deploy e escala o barramento.
O resto, rapidamente
- A identidade é um metadado opcional, por mensagem. A chave
clientInfode_metano lado da requisição é opcional (o par obrigatório éprotocolVersion+clientCapabilities), eserverInfosaiu do corpo do resultado deserver/discover: em vez disso, os servidores o carimbam no_metade todo resultado da era 2026 (especificação #3002). O SDK sempre carimba;client.server_infoéNonequando um servidor não se identifica (por exemplo, um middleware removeu a chave). O Server de baixo nível mostra o carimbo no tráfego real. - As requisições são roteáveis sem fazer parse do corpo. Requisições HTTP modernas carregam
Mcp-Method(e, para as três chamadas no estilo de ferramenta,Mcp-Name); uma propriedade do schema de entrada de uma ferramenta anotada comx-mcp-headeré espelhada em um cabeçalhoMcp-Param-*e conferida pelo servidor (SEP-2243). Gateways e rate limiters podem rotear só pelos cabeçalhos; o Guia de migração tem as regras. - Os resultados carregam dicas de cache. Resultados de listagem e de leitura declaram
ttlMsecacheScope(SEP-2549); você os define por método comcache_hints=, eClientos respeita com um cache de respostas embutido. Um servidor que não envia dicas (todo servidor pré-2026) vê tráfego idêntico, sem cache. Dicas de cache. - Extensões são de primeira classe. Servidores e clientes declaram conjuntos opcionais de capacidades sob identificadores em DNS reverso (SEP-2133); a extensão embutida
Apps(MCP Apps) é a referência. Extensões e MCP Apps. - Os códigos de erro foram padronizados. Um recurso inexistente é
-32602com a URI emerror.data, e os novos códigos reservados pela especificação aparecem como-32020(cabeçalho divergente),-32021(capacidade obrigatória ausente) e-32022(versão de protocolo não suportada). Solução de problemas é organizada pelas mensagens exatas. - A autorização ficou mais difícil de usar errado. O cliente valida o
issretornado com o código de autorização (RFC 9207; o seucallback_handleragora retorna umAuthorizationCodeResult), enviaapplication_typequando se registra, e nunca reutiliza credenciais em um servidor de autorização diferente. Novidade no lado corporativo: o fluxo de asserção de identidade da SEP-990. O Guia de migração lista cada mudança de OAuth; OAuth para clientes e Asserção de identidade são as páginas. - Todo servidor é rastreável. O OpenTelemetry vem ativado por padrão como middleware: toda requisição ganha um span de servidor, sem custo até o processo configurar um exportador. Quando as duas pontas rodam o SDK, o cliente também propaga o contexto de trace W3C em
_meta, então os traces se conectam. OpenTelemetry.
Atualizando a partir da v1?
- O Guia de migração é a lista completa e exata do que mudar; esta página foi o porquê.
- A v1.x não vai a lugar nenhum. Ela entra em manutenção, continua recebendo correções críticas e patches de segurança, e nada no lançamento da especificação 2026-07-28 a quebra; a documentação dela fica em /v1/. Se você publica uma biblioteca que depende de
mcpe ainda não está pronto para migrar, mantenha um limite superior (por exemplomcp>=1.28,<2) para que uma resolução sem versão fixada fique na 1.x. - Algo mal-acabado, confuso ou quebrado? Envie feedback da v2; tudo é lido.