MÓDULO 3.8

⚠️ Tratamento de Erros

Aprenda a lidar com falhas de forma elegante — validação, try/except, timeouts e logging.

6
Tópicos
25
Minutos
Intermediário
Nível
Prático
Tipo
1

⚠️ Tipos de Erros em MCP

Servers MCP estão sujeitos a diversos tipos de falhas. Compreender cada categoria de erro é o primeiro passo para tratá-los adequadamente. Um bom server não é aquele que nunca falha — é aquele que falha de forma previsível, informativa e recuperável.

💎 Conceito Principal

Os erros em um server MCP se dividem em quatro categorias principais: erros de entrada (parâmetros inválidos, tipos errados, valores fora do range esperado); erros de execução (bugs no código, divisão por zero, chave inexistente em dicionário); erros de rede (API externa fora do ar, timeout de conexão, DNS não resolvido); e erros de permissão (acesso negado a arquivo, token expirado, credenciais inválidas). Cada tipo exige uma estratégia de tratamento diferente.

📊 Dados e Pesquisa

Estudos de engenharia de software mostram que mais de 60% dos incidentes em produção são causados por tratamento inadequado de erros — não pelos erros em si. Em servers MCP, isso é ainda mais crítico porque o modelo de IA depende de mensagens de erro claras para informar o usuário sobre o que aconteceu e sugerir próximos passos. Um stack trace cru não ajuda ninguém.

📋 Categorias de erros
Erros de Entrada

Parâmetros inválidos, tipos errados, valores fora do range. Detectáveis antes de executar a lógica principal. Prevenidos com validação.

Erros de Execução

Bugs no código, KeyError, IndexError, divisão por zero. Acontecem durante o processamento. Prevenidos com try/except e testes.

Erros de Rede

API fora do ar, timeout, DNS não resolvido. Imprevisíveis e transitórios. Tratados com retry e mensagens claras.

Erros de Permissão

Acesso negado, token expirado, credenciais inválidas. Requerem ação do usuário ou admin. Mensagem clara sobre o que falta.

2

🛡️ Validação de Entrada

A primeira linha de defesa contra erros é a validação de entrada. Antes de processar qualquer dado, verifique se os parâmetros estão corretos — tipo, formato, range, presença. Nunca confie nos dados que chegam. Retorne mensagens claras quando algo estiver errado, para que o modelo possa informar o usuário de forma útil.

💎 Conceito Principal

Validação robusta no início da tool evita erros cascata:

@mcp.tool()
async def buscar_usuario(id: int) -> str:
# Validar tipo e range
if id <= 0:
return "Erro: ID deve ser um número positivo."
if id > 999999:
return "Erro: ID fora do range válido (1-999999)."
# Agora sim, processar...
❌ Evitar
  • Confiar cegamente nos dados recebidos
  • Validar apenas o tipo, ignorando range e formato
  • Mensagens de erro genéricas ("Erro interno")
  • Deixar a validação para o final do processamento
✅ Fazer
  • Validar todos os parâmetros no início da função
  • Checar tipo, range, formato e presença
  • Retornar mensagem clara dizendo o que está errado
  • Incluir o valor esperado na mensagem de erro
🚨 Alerta

Nunca exponha dados sensíveis em mensagens de erro. Evite retornar caminhos de arquivo do servidor, nomes de variáveis de ambiente, strings de conexão de banco de dados ou qualquer informação que possa ser usada para explorar o sistema. Mensagens de erro devem ser úteis para o usuário, não para um atacante.

3

🔄 Try/Except em Tools

Mesmo com validação perfeita, erros de execução podem acontecer. A estrutura try/except é sua rede de segurança — ela captura exceções antes que derrubem o server e permite retornar mensagens de erro úteis em vez de stack traces incompreensíveis.

💎 Conceito Principal

Envolva a lógica principal em try/except com exceções específicas:

@mcp.tool()
async def processar_dados(dados: str) -> str:
try:
resultado = json.loads(dados)
valor = resultado["chave"]
return f"Valor encontrado: {valor}"
except json.JSONDecodeError:
return "Erro: dados não são JSON válido."
except KeyError as e:
return f"Erro: chave {e} não encontrada nos dados."
except Exception as e:
return f"Erro inesperado: {type(e).__name__}."
💡 Dica Prática

Sempre capture exceções específicas antes da genérica. Comece com os erros mais prováveis (ValueError, KeyError, json.JSONDecodeError) e termine com except Exception como rede de segurança final. Nunca use except: sem tipo — isso captura até KeyboardInterrupt e SystemExit.

❌ Evitar
  • except: sem tipo de exceção
  • Retornar stack trace completo ao modelo
  • Silenciar erros com pass
  • Um único except genérico para tudo
✅ Fazer
  • Capturar exceções específicas primeiro
  • Retornar mensagem de erro legível e útil
  • Logar o erro completo (ver tópico 6)
  • Exception genérica como fallback final
4

📡 Erros de Rede

Quando suas tools fazem requisições HTTP a APIs externas, falhas de rede são inevitáveis. O servidor da API pode estar fora do ar, a conexão pode expirar, ou a resposta pode ter um status de erro. Cada tipo de falha de rede exige tratamento específico com mensagens que ajudem o modelo a informar o usuário sobre o que aconteceu.

💎 Conceito Principal

Com httpx, os principais erros de rede são:

  • httpx.ConnectError — não conseguiu conectar ao servidor (DNS, porta fechada, host inacessível)
  • httpx.TimeoutException — conexão ou leitura excedeu o tempo limite
  • httpx.HTTPStatusError — servidor respondeu com status 4xx ou 5xx

Trate cada um separadamente para dar mensagens precisas ao modelo.

📋 Tipos de erro de rede e como tratar
ConnectError

Servidor inacessível. Mensagem: "Não foi possível conectar ao serviço X. Verifique se o serviço está online." Retry pode ajudar se for transitório.

TimeoutException

Tempo esgotado. Mensagem: "A requisição ao serviço X demorou mais que o esperado." Considerar aumentar timeout ou retry com backoff.

HTTPStatusError

Resposta com erro. 404 = recurso não encontrado, 401/403 = não autorizado, 500 = erro no servidor externo. Adaptar mensagem ao status code.

🚨 Alerta

Cuidado com retry infinito. Se a API está fora do ar, tentar novamente sem limites vai travar sua tool. Use retry com backoff exponencial e limite máximo de tentativas (ex: 3 tentativas com espera de 1s, 2s, 4s). Se todas falharem, retorne uma mensagem clara informando que o serviço está temporariamente indisponível.

5

⏱️ Timeouts

Configurar timeouts é essencial em servers MCP. O modelo de IA espera a resposta da tool para continuar — se a tool travar em uma requisição HTTP sem timeout, o modelo fica parado indefinidamente. Sempre defina limites de tempo para operações que podem demorar.

💎 Conceito Principal

Sempre configure timeout nas requisições HTTP:

async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.get(url)

O valor timeout=10.0 significa que a requisição será cancelada se demorar mais de 10 segundos. Para APIs rápidas, 5-10 segundos é suficiente. Para operações pesadas, pode ser necessário 30 segundos ou mais — mas sempre com um limite definido.

💡 Dica Prática

Você pode configurar timeouts granulares com httpx.Timeout(connect=5.0, read=10.0, write=5.0, pool=5.0). Isso permite limites diferentes para conexão, leitura, escrita e pool. Na maioria dos casos, timeout=10.0 como valor único é suficiente e mais simples de manter.

❌ Evitar
  • Requisições HTTP sem timeout (padrão pode ser infinito)
  • Timeouts muito longos (60s+) sem justificativa
  • Ignorar TimeoutException e deixar o server travar
  • Timeout diferente para cada tool sem padrão
✅ Fazer
  • Definir timeout em toda requisição HTTP
  • Valores razoáveis: 5-15s para APIs padrão
  • Capturar TimeoutException com mensagem clara
  • Padronizar timeouts em constantes do projeto
6

📋 Logging e Debug

Mensagens de erro retornadas ao modelo devem ser simples e úteis. Mas para você, o desenvolvedor, precisa de detalhes completos para diagnosticar problemas. É aí que entra o logging — registrar erros detalhados para stderr sem interferir no protocolo MCP que usa stdout.

💎 Conceito Principal

Configure logging para registrar erros detalhados em stderr:

import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler()] # stderr por padrão
)
logger = logging.getLogger("mcp-server")
# Dentro de uma tool:
except Exception as e:
logger.error(f"Erro em buscar_usuario: {e}", exc_info=True)
return "Erro ao buscar usuário. Tente novamente."

O exc_info=True inclui o stack trace completo no log — essencial para debug. O protocolo MCP usa stdout para comunicação, então logs em stderr não interferem.

💡 Dica Prática

Use níveis de log adequados: DEBUG para detalhes de desenvolvimento, INFO para operações normais (tool chamada, parâmetros recebidos), WARNING para situações suspeitas mas não críticas, ERROR para falhas que impedem a operação. Em produção, configure o nível para INFO; em desenvolvimento, use DEBUG.

📊 Dados e Pesquisa

O MCP Inspector mostra os logs do server na sua interface, facilitando o debug durante desenvolvimento. Logs estruturados (com timestamp, nível e nome do logger) são 3x mais rápidos de diagnosticar do que prints soltos, segundo estudos de engenharia de software. Invista tempo configurando logging adequado desde o início — você agradecerá quando surgir o primeiro bug em produção.

📝 Resumo do Módulo

  • Erros em MCP se dividem em quatro categorias: entrada, execução, rede e permissão.
  • Validação de entrada é a primeira linha de defesa — cheque tipo, range e formato antes de processar.
  • Try/except com exceções específicas captura erros e retorna mensagens úteis em vez de stack traces.
  • Erros de rede (ConnectError, TimeoutException, HTTPStatusError) precisam de tratamento individual.
  • Timeouts são obrigatórios em requisições HTTP — o modelo não pode ficar esperando para sempre.
  • Logging em stderr preserva o protocolo MCP e fornece detalhes essenciais para debug.

Próximo Módulo: 3.9 — Testando com MCP Inspector