Introduzindo tratamento de erros no json-rpc
Para responder à pergunta do vídeo anterior — qual protocolo vamos utilizar e como essa arquitetura altera a resposta de erro —, em aproximadamente 90% dos casos trabalharemos com erros no JSON-RPC. Por isso, vamos detalhar como o JSON-RPC trata erros.
Há alguns cenários em que o erro virá pelo protocolo HTTP, quando o utilizamos como camada de transporte. No entanto, de forma geral, a mensagem de erro estará apenas no protocolo JSON-RPC. Isso significa que o HTTP não sinalizará erros na maioria das vezes. Se olharmos apenas os status do HTTP, veremos respostas com 200 e poderemos concluir erroneamente que não há erro, quando, na verdade, o erro estará encapsulado no corpo da resposta, isto é, no body (corpo) da requisição/resposta.
Definindo o objeto de erro no json-rpc
Como o JSON-RPC organiza os erros? O JSON-RPC define um objeto padrão de erro. Toda a comunicação é feita em JSON, um formato que define objetos e propriedades. Teremos sempre um atributo error, que é um objeto com code, message e data. O campo data é opcional e carrega metadados do erro, permitindo incluir mais detalhes sobre aquele erro específico. Já code e message são os atributos básicos do erro, em linha com a ideia de código e mensagem que também conhecemos no HTTP.
Para ilustrar, um exemplo de objeto de erro no JSON-RPC poderia ser:
error: {
code: -32602,
message: "Unknown tool: invalid_tool_name",
data: {'dados do erro'}
}
Note que code, message e data seguem a estrutura descrita.
Relembrando classes de status http
Se estivermos muito acostumados ao protocolo HTTP, podemos estranhar os valores de code no JSON-RPC, pois eles não seguem as classes 4xx e 5xx do HTTP. No HTTP, em geral:
- Quando há erro de requisição do cliente (por exemplo, algo feito de forma incorreta), utilizamos a classe 4xx. Exemplo: o cliente pediu um recurso ao qual não tem acesso, retornamos 403, Forbidden (Proibido). Se o recurso não existe, retornamos 404, Not Found (Não encontrado).
- Quando a requisição está correta, mas houve problema ao processar no servidor, utilizamos a classe 5xx. Exemplo: 500, Internal Server Error (Erro interno do servidor).
- Se há falha na comunicação por problemas do gateway entre cliente e servidor, retornamos 502, Bad Gateway (Gateway incorreto).
Esse é o comportamento ao qual estamos acostumados e que o HTTP define, sendo muito comum quando trabalhamos com uma API REST, em que cliente e servidor se comunicam pela internet via HTTP.
Caso não haja familiaridade com o tratamento de erros no HTTP, existem cursos específicos na Alura. Basta procurar nas formações de REST, independente da linguagem. No nosso caso, seria o de Node.js, onde veremos em detalhes como o HTTP trabalha os erros.
Evitando depender de status http
Como mencionamos, em 90% dos casos não usaremos o HTTP para transmitir o erro dentro do nosso protocolo MCP, porque seguiremos o JSON-RPC. Assim, os códigos e as formas de comunicação de erro obedecerão ao protocolo do JSON-RPC, que trafega no corpo da requisição. Portanto, não devemos nos basear apenas nos códigos de status do HTTP para identificar erros quando estivermos utilizando JSON-RPC sobre HTTP.
Precisamos utilizar os códigos definidos pelo JSON-RPC (Chamada de Procedimento Remoto em JSON). Eles podem parecer estranhos no início, mas é apenas uma questão de costume. Trata-se de um intervalo numérico; não há nada fora do comum além do número que aparece na resposta. Com o tempo, nós nos acostumamos.
Listando códigos padrão do json-rpc
Todos os códigos ficam entre os números -32.768 e -32.000. São valores negativos mesmo — com sinal de menos — conforme definido pela especificação.
- Para JSON inválido (erro de formatação do JSON enviado pelo cliente), retornamos -32.700.
- Se o JSON está correto, mas a requisição é inválida, retornamos -32.600.
- Se um método foi requisitado e não existe (por exemplo, uma ferramenta ausente no servidor), retornamos -32.601.
- Se o método existe, mas os parâmetros são inválidos, retornamos -32.602.
- O intervalo entre -32.000 e -32.099 é reservado para erros de servidor customizados. Nesse intervalo, podemos definir erros específicos da aplicação.
Esses são os códigos que vamos utilizar quando recebemos um erro ou quando precisamos enviar um erro para o cliente. A especificação traz mais detalhes e tipos de erro possíveis, mas os listados acima são os mais comuns e, por isso, os destacamos aqui.
Tratando erros de execução no mcp
Além do objeto de erro, precisamos ter atenção ao protocolo MCP para erros de execução (erros que acontecem entre cliente e servidor). Embora o JSON-RPC defina a estrutura do objeto de erro, a documentação do MCP orienta a não retornar apenas esse objeto em casos de execução. Nesses cenários, o objeto de retorno deve conter um resultado e, em vez do objeto de erro, devemos enviar um objeto de conteúdo com a propriedade isError definida como true.
Veja um exemplo de retorno de execução com conteúdo marcando erro:
result: {
resultType: "complete",
content: [
{
type: "text",
text: "Mensagem de erro"
}
],
isError: true
}
Perceba que o indicador de erro está em isError: true dentro do conteúdo do resultado.
Por que o protocolo MCP adota esse formato? Não temos uma justificativa clara. Para nós, faria mais sentido retornar um objeto de erro com a propriedade isError: true, mas a especificação determina que retornemos o conteúdo com isError: true.
Classificando erros de execução
Erros de execução incluem:
- Falha na chamada de API (Interface de Programação de Aplicações).
- Erro de lógica de negócio.
- Validação de input (entrada).
Basicamente, consideramos como erros de execução aqueles que podemos customizar dentro do intervalo de -32.000 a -32.099, conforme descrito acima.
Diferenciando erros entre camadas
Como o protocolo MCP, o JSON-RPC e o HTTP (Protocolo de Transferência de Hipertexto) coexistem, é possível haver confusões, pois o erro pode aparecer em formatos diferentes dependendo da camada.
Para deixar claro:
- Em erros de autorização (por exemplo, de login (acesso) e senha, autenticação de usuário), o erro deve vir na camada de HTTP. Não vamos tratar esse caso especificamente no curso, mas é uma exigência do protocolo MCP.
- Em erros de execução, o retorno deve seguir o formato com objeto de conteúdo e a propriedade
isError: true. - Em erros de validação de entrada ou de requisição, o JSON-RPC deve enviar o objeto de erro correspondente.
Há muitos detalhes a observar, mas nós vamos, aos poucos, entendendo e fazendo tudo funcionar.
Em grande parte do que vamos trabalhar, utilizaremos JSON-RPC. Em alguns casos específicos, vamos trabalhar com HTTP ou com o objeto de conteúdo exigido pelo MCP.