Erros do Servidor MCP
Como tratar erros retornados pelo servidor foura-mcp.
Toda resposta de erro de qualquer uma das três ferramentas (foura_single, foura_proxy, foura_browser) é estruturada. Agentes de LLM podem ler o campo code para lógica de nova tentativa sem analisar texto corrido.
Formato do envelope
Todo erro (isError: true) carrega um bloco structuredContent. Campos mínimos em cada erro:
{
"service": "single | proxy | browser",
"code": "rate_limited",
"error": "Rate limit exceeded"
}
Em erros de upstream com status HTTP, status também está presente. Em erros de rate limit e capacidade, o envelope adiciona retryAfter, current.{concurrency, rpm} e limits.{maxConcurrency, maxRpm}, o mesmo formato dos erros da API REST subjacentes.
Valores estáveis de code
| Código | HTTP | Significado | Seguro para tentar novamente? |
|---|---|---|---|
ssrf_blocked |
n/a | IP de destino em um intervalo privado ou reservado (RFC 5735, RFC 6598, IPv6 reservado) | Não, altere a URL |
upstream_non_json |
varia | O upstream retornou um corpo que não era um JSON válido | Talvez, investigue |
output_validation_failed |
n/a | O outputSchema do servidor MCP rejeitou a resposta do upstream (bug do servidor ou formato inesperado do upstream) |
Talvez, relate |
bad_request |
400 | Formato de entrada rejeitado pela API FourA | Não, corrija os argumentos |
auth_failed |
401 | Chave ausente, inválida ou desativada | Não, corrija a chave |
forbidden |
403 | O destino rejeitou a request (anti-bot, geo-block) | Não, ou mude para foura_proxy |
not_found |
404 | A URL ou endpoint de destino não existe | Não |
rate_limited |
429 | Limite de RPM por chave atingido | Sim, aguarde retryAfter segundos |
at_capacity |
503 | Limite de concorrência atingido (current.concurrency > limits.maxConcurrency) |
Sim, aguarde retryAfter segundos |
service_disabled |
503 | Serviço desativado para sua conta (plano ou manutenção) | Contate o suporte |
service_unavailable |
503 | 503 genérico do upstream | Sim, backoff curto |
upstream_error |
500+ | 5xx do upstream | Sim, backoff exponencial |
upstream_client_error |
4xx | Outro 4xx não coberto acima | Geralmente não |
upstream_unknown |
outro | Defensivo, não deve ocorrer na prática | Investigue |
Erros de nível HTTP do servidor MCP
Algumas falhas ocorrem na camada de transporte do MCP, antes de qualquer ferramenta ser chamada. Elas retornam erros JSON-RPC brutos (sem structuredContent):
| HTTP | Quando | O que você vê |
|---|---|---|
| 400 | Header MCP-Protocol-Version não suportado |
Unsupported MCP-Protocol-Version: <value>. Supported: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07. |
| 401 | Header Authorization ausente ou malformado |
Erro JSON-RPC + WWW-Authenticate: Bearer realm="foura-mcp", resource_metadata="https://foura.ai/docs/mcp/server#auth" |
| 403 | Header Origin ou Host não permitido (defesa contra DNS-rebinding, CVE-2025-66414) |
Origin <value> is not in the allowlist ou Host <value> is not in the allowlist |
| 405 | GET ou DELETE em /mcp (modo stateless) |
Method not allowed in stateless mode. Use POST /mcp. |
| 413 | Corpo da request > 256 KB | Padrão do Express 413 |
As allowlists para 403 são configuráveis por variáveis de ambiente para self-hosters via FOURA_MCP_ALLOWED_HOSTS e FOURA_MCP_ALLOWED_ORIGINS.
Estratégia de nova tentativa
Três categorias:
- Aguardar + tentar novamente:
rate_limited,at_capacity,service_unavailable,upstream_error. RespeiteretryAfterquando presente (indicação do servidor, em segundos). Use backoff exponencial com jitter caso contrário. - Não tentar novamente, corrigir entrada:
bad_request,auth_failed,not_found,ssrf_blocked. - Mudar de ferramenta:
forbiddenemfoura_single→ escalone parafoura_proxy. Se a página também precisar de JavaScript, usefoura_browser. Se ofoura_browserrelatarforbidden, encadeie comfoura_proxyprimeiro e passe o ID deproxyretornado emfoura_browser.proxy.
Exemplo de nova tentativa (TypeScript, lado do MCP)
async function callWithRetry(call: () => Promise<any>, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const r = await call();
if (!r.isError) return r;
const code = r.structuredContent?.code;
const wait = r.structuredContent?.retryAfter ?? Math.min(2 ** attempt, 30);
if (["rate_limited", "at_capacity", "service_unavailable", "upstream_error"].includes(code)) {
await new Promise((res) => setTimeout(res, wait * 1000));
continue;
}
// Non-retryable, surface to caller
throw new Error(`${code}: ${r.structuredContent?.error}`);
}
throw new Error("max retries exceeded");
}
Relacionado
- Servidor MCP, as três ferramentas e seus schemas
- Receitas MCP, prompts de fluxo de trabalho enviados com o servidor
- Erros da API, mesmo envelope na camada da API REST subjacente