Errores del servidor MCP
Cómo manejar los errores devueltos por el servidor foura-mcp.
Cada respuesta de error de cualquiera de las tres herramientas (foura_single, foura_proxy, foura_browser) está estructurada. Los agentes LLM pueden leer el campo code para la lógica de reintento sin necesidad de analizar prosa.
Estructura del contenedor
Cada error (isError: true) contiene un bloque structuredContent. Campos mínimos en cada error:
{
"service": "single | proxy | browser",
"code": "rate_limited",
"error": "Rate limit exceeded"
}
En caso de errores de upstream con estado HTTP, status también está presente. En errores de rate limit y de capacidad, el contenedor añade retryAfter, current.{concurrency, rpm} y limits.{maxConcurrency, maxRpm}, con la misma estructura que los errores de la API REST subyacentes.
Valores de code estables
| Code | HTTP | Significado | ¿Seguro para reintentar? |
|---|---|---|---|
ssrf_blocked |
n/a | IP de destino en un rango privado o reservado (RFC 5735, RFC 6598, IPv6 reservado) | No, cambie la URL |
upstream_non_json |
varía | El upstream devolvió un cuerpo que no era un JSON válido | Quizás, investigar |
output_validation_failed |
n/a | El outputSchema del servidor MCP rechazó la respuesta del upstream (error del servidor o estructura inesperada del upstream) |
Quizás, reportar |
bad_request |
400 | Estructura de entrada rechazada por la API de FourA | No, corrija los argumentos |
auth_failed |
401 | Clave faltante, no válida o desactivada | No, corrija la clave |
forbidden |
403 | El destino rechazó la request (anti-bot, bloqueo geográfico) | No, o cambie a foura_proxy |
not_found |
404 | La URL o el endpoint de destino no existen | No |
rate_limited |
429 | Límite de RPM por clave alcanzado | Sí, espere retryAfter segundos |
at_capacity |
503 | Límite de concurrencia alcanzado (current.concurrency > limits.maxConcurrency) |
Sí, espere retryAfter segundos |
service_disabled |
503 | Servicio deshabilitado para su cuenta (plan o mantenimiento) | Contacte al soporte técnico |
service_unavailable |
503 | 503 genérico del upstream | Sí, retroceso corto |
upstream_error |
500+ | 5xx del upstream | Sí, retroceso exponencial |
upstream_client_error |
4xx | Otro 4xx no cubierto anteriormente | Normalmente no |
upstream_unknown |
otro | Defensivo, no debería ocurrir en la práctica | Investigar |
Errores a nivel HTTP del servidor MCP
Algunos fallos ocurren en la capa de transporte de MCP, antes de llamar a cualquier herramienta. Estos devuelven errores JSON-RPC sin procesar (sin structuredContent):
| HTTP | Cuándo | Lo que ve |
|---|---|---|
| 400 | Header MCP-Protocol-Version no soportado |
Unsupported MCP-Protocol-Version: <value>. Supported: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07. |
| 401 | Header Authorization faltante o malformado |
JSON-RPC error + WWW-Authenticate: Bearer realm="foura-mcp", resource_metadata="https://foura.ai/docs/mcp/server#auth" |
| 403 | Header Origin o Host no permitido (defensa contra DNS-rebinding, CVE-2025-66414) |
Origin <value> is not in the allowlist o Host <value> is not in the allowlist |
| 405 | GET o DELETE en /mcp (modo sin estado) |
Method not allowed in stateless mode. Use POST /mcp. |
| 413 | Cuerpo de la request > 256 KB | 413 predeterminado de Express |
Las listas de permitidos para 403 son configurables mediante variables de entorno para quienes realizan autohospedaje a través de FOURA_MCP_ALLOWED_HOSTS y FOURA_MCP_ALLOWED_ORIGINS.
Estrategia de reintento
Tres grupos:
- Esperar + reintentar:
rate_limited,at_capacity,service_unavailable,upstream_error. RespeteretryAftercuando esté presente (indicación del servidor, en segundos). Utilice retroceso exponencial con jitter cuando no lo esté. - No reintentar, corregir entrada:
bad_request,auth_failed,not_found,ssrf_blocked. - Cambiar de herramienta:
forbiddenenfoura_single→ escalar afoura_proxy. Si la página también necesita JavaScript,foura_browser. Sifoura_browserreportaforbidden, encadénelo primero confoura_proxyy pase el ID deproxydevuelto afoura_browser.proxy.
Ejemplo de reintento (TypeScript, lado de 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, las tres herramientas y sus esquemas
- Recetas de MCP, prompts de flujo de trabajo incluidos con el servidor
- Errores de la API, el mismo contenedor en la capa de la API REST subyacente