Pular para conteúdo

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:

server.py
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á em content, 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.

server.py
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, sem is_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, message e data chegam intactos. INVALID_PARAMS é -32602; mcp.types exporta 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 MCPError quando 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.

server.py
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=True com a sua mensagem em content. 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, message e data sobrevivem intactos.
  • A pergunta que decide: um modelo mais esperto teria evitado isso? Sim -> exceção. Não -> MCPError.
  • ResourceNotFoundError em um handler de recurso -> o -32602 do protocolo, com a URI em data.
  • Argumentos inválidos são rejeitados com base no schema antes de a sua função executar; você não dá raise para eles.
  • from mcp import MCPError; as constantes de código de erro vêm de mcp.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.