Errores del servidor MCP
Errores del servidor MCP
Cómo manejar los errores devueltos por el servidor foura-mcp.
Cada respuesta de error de cualquiera de las cuatro herramientas (foura_auto, 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 texto libre.
Estructura del envelope
Cada error (isError: true) incluye un bloque structuredContent. Campos mínimos en cada error:
{
"service": "auto | single | proxy | browser",
"code": "rate_limited",
"error": "Rate limit exceeded"
}
En errores de upstream con estado HTTP, status también está presente. Cuando la cuota compartida de la plataforma rechaza una llamada, el sobre añade retryAfter, current.{concurrency, rpm} y limits.{maxConcurrency, maxRpm}, con la misma estructura que los errores subyacentes de la API REST.
Cuando uno de los límites propios de tu plan rechaza una llamada, el código ES ese límite: plan_limit_ seguido de credits, bandwidth, rate, concurrency, browser_daily, premium o feature. retryAfter indica el tiempo de espera cuando esperar lo soluciona, y está ausente en límites que una espera no puede restablecer, como una función no incluida en el plan. plan_limit_browser_daily tampoco incluye retryAfter: se restablece a medianoche UTC.
En foura_auto, un límite del plan alcanzado dentro de su escala se devuelve como rate_limited o forbidden, con el código del plan en reason.
Valores estables de code
| Code | HTTP | Meaning | Retry safe? |
|---|---|---|---|
ssrf_blocked |
n/a | El destino es una dirección privada o reservada (RFC 5735, RFC 6598, IPv6 reservada), la URL no es http(s) o su nombre de host no se resolvió | No, verifica la URL. Una resolución que falló brevemente se puede reintentar |
upstream_non_json |
varía | El upstream devolvió un body que no era un JSON válido | Tal vez, investiga |
output_validation_failed |
n/a | El outputSchema del servidor MCP rechazó la respuesta del upstream, o la herramienta no pudo completar la llamada (no hay API key configurada, API inaccesible) |
Tal vez: verifica la configuración, luego reporta |
bad_request |
400 | Estructura de entrada rechazada por la API de FourA | No, corrige los argumentos |
auth_failed |
401 | La API key de FourA falta, no es válida o está desactivada; esto no se refiere a las credenciales del sitio de destino | No, corrige la key de FourA |
forbidden |
403 | El destino rechazó la request (un bloqueo del sitio, una restricción por país) | No, o cambia a foura_proxy |
not_found |
404 | La URL o el endpoint de destino no existe | No |
rate_limited |
429 | El límite compartido por minuto de la plataforma, o un 429 del destino que tu validate rechazó. En foura_auto también pueden ser los créditos, el tráfico o el rate limit de tu plan (consulta reason) |
Sí, espera retryAfter cuando esté presente, de lo contrario aplica backoff |
at_capacity |
503 | Límite de concurrencia alcanzado (current.concurrency > limits.maxConcurrency) |
Sí, espera retryAfter segundos |
service_disabled |
503 | El servicio está desactivado por mantenimiento. Una herramienta que tu plan no incluye devuelve plan_limit_feature |
Contacta a soporte |
service_unavailable |
503 | 503 genérico del upstream | Sí, backoff corto |
upstream_error |
500+ o 0 | El destino respondió con un error de servidor, o en foura_proxy, foura_browser y foura_auto nunca respondieron |
Sí, backoff exponencial |
upstream_client_error |
4xx | Otro 4xx no cubierto arriba | Por lo general, no |
upstream_unknown |
otro | La request se ejecutó pero no produjo una respuesta aceptada: en foura_single el destino nunca respondió (timeout, conexión rechazada), y en cualquier herramienta tu validate rechazó una respuesta 2xx o 3xx. Lee status y error |
Investiga |
no_eligible_proxy |
n/a | Ningún proxy coincide con la allowlist estricta de exitCountries; details.exitCountries contiene el scope normalizado |
Reintenta más tarde; cambia el scope solo de forma explícita |
plan_limit_credits |
429 | Los créditos mensuales de tu plan se han agotado | Sí, después de retryAfter, o cambia de plan |
plan_limit_bandwidth |
429 | El límite de tráfico de tu plan se ha agotado para este periodo de facturación | Sí, después de retryAfter, o cambia de plan |
plan_limit_rate |
429 | Requests por minuto de tu plan para ese endpoint | Sí, después de retryAfter |
plan_limit_concurrency |
429 | Requests simultáneas de tu plan para ese endpoint | Sí, después de retryAfter |
plan_limit_browser_daily |
429 | El límite diario de Browser de tu plan se ha agotado | Sí, mañana, o usa foura_single / foura_proxy |
plan_limit_premium |
403 | Enviaste exitClass: "premium" en un plan que no incluye salidas premium |
No, elimina el parámetro o cambia de plan |
plan_limit_feature |
403 | El plan no incluye ese endpoint o esa funcionalidad | No, cambia de plan |
Errores a nivel HTTP del servidor MCP
Algunos fallos ocurren en la capa de transporte de MCP, antes de que se llame a cualquier tool. Estos devuelven errores JSON-RPC puros (sin structuredContent):
| HTTP | Cuándo | Lo que ves |
|---|---|---|
| 400 | Header MCP-Protocol-Version no compatible |
Unsupported MCP-Protocol-Version: <value>. Supported: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07. |
| 401 | Una llamada a tool o lectura de recurso sin API key. Listar tools y prompts funciona sin ella | Error JSON-RPC + WWW-Authenticate: Bearer realm="foura-mcp" |
| 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 stateless) |
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 se pueden configurar mediante variables de entorno para implementaciones self-hosted a través de FOURA_MCP_ALLOWED_HOSTS y FOURA_MCP_ALLOWED_ORIGINS.
Perfiles de navegador rechazados
Un perfil de navegador que el catálogo no puede ofrecer, o un perfil enviado con unblocker establecido en false, se devuelve como un fallo upstream con el motivo en error y la request nunca sale de FourA. El mensaje indica lo que está disponible, así que reintenta con una de las combinaciones listadas en lugar de usar la misma.
Estos son rechazos, no interrupciones del servicio: reintentar la request idéntica no puede tener éxito y no se utilizó ningún otro navegador en su lugar.
Estrategia de reintento
Cinco categorías:
- Tu propio plan lo rechazó, no el objetivo: cualquier código
plan_limit_*. El mismo trabajo a través de otra tool también será rechazado, por lo que cambiar de endpoint solo consume tiempo. Espera a que termineretryAftercuando exista uno; de lo contrario, el plan debe cambiar.plan_limit_premiumes el que puedes solucionar tú mismo, eliminandoexitClass. - Esperar y reintentar:
rate_limited,at_capacity,service_unavailable,upstream_error. RespetaretryAftercuando esté presente. Usa backoff exponencial con jitter cuando no esté disponible. No respondas reenviando todas las llamadas a tools en cola a la vez: reduce en su lugar cuántas ejecutas en paralelo. - Conservar el alcance y reintentar más tarde:
no_eligible_proxy. No eliminesexitCountriesni sustituyas otro país silenciosamente. Modifica o amplía la lista de permitidos únicamente cuando el usuario cambie el requisito de forma explícita. - No reintentar hasta que se corrija la entrada o la credencial:
bad_request,auth_failed,not_found,ssrf_blocked. Paraauth_failed, verifica la API key de FourA, no las credenciales del sitio objetivo. - Cambiar de tool cuando el contenido lo requiera:
forbiddenenfoura_singlepuede justificar un intento acotado defoura_proxy. Usafoura_browsercuando el contenido deseado necesite JavaScript. Tras una selección de proxy exitosa, pasa el IDproxydevuelto afoura_browser.proxyen lugar de iniciar una nueva selección.
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
- MCP Server, las cuatro herramientas y sus esquemas
- MCP Recipes, prompts de flujo de trabajo incluidos con el servidor
- API Errors, la misma estructura en la capa de la REST API subyacente
- Rate Limits, los límites de cuenta y plataforma detrás de
rate_limitedyat_capacity