Erreurs serveur MCP

Comment gérer les erreurs renvoyées par le serveur foua-mcp.

Chaque response d'erreur de l'un des trois outils (foura_single, foura_proxy, foura_browser) est structurée. Les agents LLM peuvent lire le champ code pour la logique de retry sans analyser de texte brut.

Structure de l'enveloppe

Chaque erreur (isError: true) contient un bloc structuredContent. Champs minimaux pour chaque erreur :

{
  "service": "single | proxy | browser",
  "code": "rate_limited",
  "error": "Rate limit exceeded"
}

Pour les erreurs upstream avec un statut HTTP, status est également présent. Pour les erreurs de rate limit et de capacité, l'enveloppe ajoute retryAfter, current.{concurrency, rpm} et limits.{maxConcurrency, maxRpm}, avec la même structure que les erreurs de l'API REST.

Valeurs de code stables

Code HTTP Signification Retry possible ?
ssrf_blocked n/a IP cible dans une plage privée ou réservée (RFC 5735, RFC 6598, IPv6 réservé) Non, modifiez l'URL
upstream_non_json varie L'upstream a renvoyé un corps qui n'était pas du JSON valide Peut-être, investiguez
output_validation_failed n/a L'outputSchema du serveur MCP a rejeté la response upstream (bug du serveur ou structure upstream inattendue) Peut-être, signalez-le
bad_request 400 Structure d'entrée rejetée par l'API FourA Non, corrigez les arguments
auth_failed 401 Clé manquante, invalide ou désactivée Non, corrigez la clé
forbidden 403 La cible a rejeté la request (anti-bot, géo-blocage) Non, ou passez à foura_proxy
not_found 404 L'URL cible ou l'endpoint n'existe pas Non
rate_limited 429 Limite de RPM par clé atteinte Oui, attendez retryAfter secondes
at_capacity 503 Limite de concurrence atteinte (current.concurrency > limits.maxConcurrency) Oui, attendez retryAfter secondes
service_disabled 503 Service désactivé pour votre compte (forfait ou maintenance) Contactez le support
service_unavailable 503 503 générique de l'upstream Oui, court backoff
upstream_error 500+ Upstream 5xx Oui, backoff exponentiel
upstream_client_error 4xx Autre 4xx non couvert ci-dessus Généralement non
upstream_unknown other Défensif, ne devrait pas se produire en pratique Investiguez

Erreurs au niveau HTTP du serveur MCP

Certains échecs se produisent au niveau de la couche de transport MCP, avant l'appel de tout outil. Ceux-ci renvoient des erreurs JSON-RPC brutes (sans structuredContent) :

HTTP Quand Ce que vous voyez
400 Header MCP-Protocol-Version non supporté Unsupported MCP-Protocol-Version: <value>. Supported: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07.
401 Header Authorization manquant ou malformé JSON-RPC error + WWW-Authenticate: Bearer realm="foura-mcp", resource_metadata="https://foura.ai/docs/mcp/server#auth"
403 Header Origin ou Host non autorisé (protection contre le DNS-rebinding, CVE-2025-66414) Origin <value> is not in the allowlist or Host <value> is not in the allowlist
405 GET ou DELETE sur /mcp (mode stateless) Method not allowed in stateless mode. Use POST /mcp.
413 Corps de la request > 256 KB Erreur 413 par défaut d'Express

Les allowlists pour 403 sont configurables via les variables d'environnement pour l'auto-hébergement via FOURA_MCP_ALLOWED_HOSTS et FOURA_MCP_ALLOWED_ORIGINS.

Stratégie de retry

Trois catégories :

  • Attendre + retry : rate_limited, at_capacity, service_unavailable, upstream_error. Respectez retryAfter lorsqu'il est présent (indication du serveur, en secondes). Utilisez un backoff exponentiel avec gigue (jitter) sinon.
  • Ne pas faire de retry, corriger l'entrée : bad_request, auth_failed, not_found, ssrf_blocked.
  • Changer d'outil : forbidden sur foura_single → passer à foura_proxy. Si la page nécessite également JavaScript, utilisez foura_browser. Si foura_browser signale forbidden, enchaînez d'abord avec foura_proxy et passez l'ID de proxy renvoyé dans foura_browser.proxy.

Exemple de retry (TypeScript, côté 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");
}

Voir aussi

  • Serveur MCP, les trois outils et leurs schémas
  • Recettes MCP, prompts de workflow fournis avec le serveur
  • Erreurs d'API, même enveloppe au niveau de la couche API REST sous-jacente
Mis à jour : 1 juillet 2026