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. RespectezretryAfterlorsqu'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 :
forbiddensurfoura_single→ passer àfoura_proxy. Si la page nécessite également JavaScript, utilisezfoura_browser. Sifoura_browsersignaleforbidden, enchaînez d'abord avecfoura_proxyet passez l'ID deproxyrenvoyé dansfoura_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