Tratando erros
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.
Uma ferramenta (tool) pode falhar de duas maneiras, e o SDK trata cada uma de forma bem diferente.
Lance uma exceção comum e é o modelo que a vê. Lance MCPError e é o protocolo que a vê.
Esta página é sobre essa escolha.
Um erro que o modelo consegue corrigir
Pegue uma ferramenta que faz uma consulta e deixe a consulta não encontrar nada:
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.tool()
def get_author(title: str) -> str:
"""Look up the author of a book in the catalog."""
if title not in CATALOG:
raise ValueError(f"No book titled {title!r} in the catalog.")
return CATALOG[title]
Não há nada de MCP nessas duas linhas. get_author lança um ValueError comum, como qualquer função Python faria.
Chame a ferramenta com um título que não está no catálogo e veja o resultado:
result.is_error # True
result.content # [TextContent(text="Error executing tool get_author: No book titled 'Nothing' in the catalog.")]
result.structured_content # None
- A requisição foi bem-sucedida. Há um resultado; nada foi lançado no lado de quem chamou.
is_erroréTrue, e a mensagem da sua exceção (prefixada com o nome da ferramenta) está emcontent, exatamente onde o modelo lê.structured_contentéNone. Uma chamada que falhou não tem valor de retorno para estruturar.
Isso é um erro de ferramenta, e é o padrão para qualquer exceção que a sua ferramenta lançar. Também é, quase sempre, o que você quer.
Quem chama a sua ferramenta é o modelo. Foi ele que escolheu os argumentos. Então um erro de ferramenta é um turno na conversa: o modelo lê "No book titled 'Nothing' in the catalog.", percebe que chutou o título errado e chama de novo com um melhor. Você escreveu um raise e ganhou um agente que se corrige sozinho.
Tip
Nunca faça return de uma mensagem de erro em uma ferramenta. Uma string retornada tem is_error=False, então, para o
modelo (e para toda interface de cliente), parece que a ferramenta funcionou e que aquela string era a resposta.
Use raise. A flag é o sinal.
Um erro que o modelo não consegue corrigir
Agora troque ValueError por MCPError.
from mcp import MCPError
from mcp.server import MCPServer
from mcp.types import INVALID_PARAMS
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.tool()
def get_author(title: str) -> str:
"""Look up the author of a book in the catalog."""
if title not in CATALOG:
raise MCPError(code=INVALID_PARAMS, message=f"No book titled {title!r} in the catalog.")
return CATALOG[title]
MCPError é o erro de protocolo do SDK. É a única exceção que o wrapper da ferramenta não captura: ela se propaga, e a requisição tools/call inteira falha com um erro JSON-RPC em vez de um resultado.
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog."
}
- Não há resultado. Sem
content, semis_error: nada para o modelo ler. - Quem recebe o erro é a aplicação host, do mesmo jeito que receberia se a ferramenta nem existisse.
code,messageedatachegam intactos.INVALID_PARAMSé-32602;mcp.typesexporta esse e os outros códigos de erro JSON-RPC (INVALID_REQUEST,INTERNAL_ERROR, ...) como constantes, para que você nunca precise digitar um número mágico.
Check
Mesma consulta, mesma falha, mas agora a chamada lança a exceção no lado do cliente em vez de retornar:
mcp.shared.exceptions.MCPError: No book titled 'Nothing' in the catalog.
A primeira versão entregou ao modelo uma frase à qual ele podia reagir. Esta não entrega nada.
Para get_author isso é estritamente pior, e é esse o ponto da próxima seção.
Qual delas lançar
Os dois caminhos respondem a duas perguntas diferentes.
- Lance qualquer exceção para uma falha de execução: aquilo que a sua ferramenta tentou fazer não funcionou. Foi o modelo que escolheu a chamada, então é o modelo que deve ver a consequência e ter a chance de se recuperar. Um título escrito errado, uma API upstream que deu timeout, uma linha que não existe: tudo erro de ferramenta.
- Lance
MCPErrorquando a própria requisição deve ser rejeitada: o cliente não tem uma capacidade da qual a sua ferramenta depende, o servidor não está em condições de atender ninguém, quem chamou pulou uma etapa obrigatória. Nenhuma nova tentativa do modelo corrige nada disso, então não há nada a ganhar entregando a mensagem a ele.
Uma pergunta decide: um modelo mais esperto teria evitado isso? Sim -> exceção comum. Não -> MCPError.
Por esse critério, a segunda versão de get_author fez a escolha errada: um título melhor resolve, então o modelo merecia ver a mensagem. Ela está ali para mostrar o mecanismo, não para recomendá-lo.
Info
MCPError fica em from mcp import MCPError e recebe code, message e um payload
data opcional. O que você colocar neles é o que o cliente recebe: o SDK repassa um
MCPError lançado tal e qual, em vez de sanitizá-lo.
Um recurso que não existe
Recursos fazem a mesma distinção, e vêm com uma exceção nomeada para o caso mais comum.
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ResourceNotFoundError
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.resource("books://{title}")
def book(title: str) -> str:
"""The catalog entry for one book."""
if title not in CATALOG:
raise ResourceNotFoundError(f"No book titled {title!r} in the catalog.")
return f"{title} by {CATALOG[title]}"
books://{title} é um template. Ele casa com qualquer título, então "a URI está bem formada" e "o livro existe" são duas perguntas diferentes, e só a sua função consegue responder à segunda.
Quando não consegue, lance ResourceNotFoundError. O SDK a transforma no erro de protocolo que a especificação atribui a um recurso ausente: -32602 com a URI requisitada em data, para que o cliente saiba qual leitura falhou.
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog.",
"data": {"uri": "books://Nothing"}
}
Repare que aqui não existe um meio-resultado com is_error=True. A leitura de um recurso ou retorna conteúdo ou falha: recursos só têm o caminho do protocolo. Templates e todo o resto sobre recursos ficam em Recursos.
Erros que você nunca lança
Um argumento inválido nunca chega à sua função.
Mande para get_author um title que não seja uma string e o SDK o rejeita com base no schema de entrada antes de chamar você, como o mesmo tipo de erro de ferramenta com is_error=True que o modelo consegue ler e corrigir. Ferramentas mostra a mesma rejeição com uma restrição Field(le=50).
Isso significa uma classe inteira de instruções raise que você não escreve: não revalide as suas próprias anotações de tipo.
Info
Tudo nesta página é o que um cliente vê, e o Client em memória com o qual você vai escrever
seus testes vê exatamente a mesma coisa. Nem raise_exceptions=True transforma um erro de ferramenta
de volta em traceback: no momento em que essa flag poderia agir, a sua exceção já virou o
resultado com is_error=True. Faça o assert no resultado. Testes cobre o padrão.
Recapitulando
- Lance qualquer exceção em uma ferramenta -> a chamada retorna
is_error=Truecom a sua mensagem emcontent. O modelo lê e pode tentar de novo. Esse é o padrão. - Lance
MCPError-> a própria chamada falha com um erro JSON-RPC. O modelo não vê nada; quem lida com isso é o host.code,messageedatasobrevivem intactos. - A pergunta que decide: um modelo mais esperto teria evitado isso? Sim -> exceção. Não ->
MCPError. ResourceNotFoundErrorem um handler de recurso -> o-32602do protocolo, com a URI emdata.- Argumentos inválidos são rejeitados com base no schema antes de a sua função executar; você não dá
raisepara eles. from mcp import MCPError; as constantes de código de erro vêm demcp.types.
Erros tratados. Isso é tudo o que um servidor expõe. O que cada handler pode ler, e fazer de volta ao cliente enquanto executa, é a próxima seção: Dentro do seu handler.
O texto exato dos erros do SDK que você tem mais chance de encontrar, o que cada um significa e a correção de um passo só para cada um estão em Solução de problemas.