MCP Server Errors

MCP Server Errors

How to handle errors returned by the foura-mcp server.

Every error response from any of the four tools (foura_auto, foura_single, foura_proxy, foura_browser) is structured. LLM agents can read the code field for retry logic without parsing prose.

Envelope shape

Every error (isError: true) carries a structuredContent block. Minimum fields on every error:

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

On upstream errors with HTTP status, status is also present. On rate-limit and capacity errors, the envelope adds retryAfter, current.{concurrency, rpm}, and limits.{maxConcurrency, maxRpm}, same shape as the underlying REST API errors.

Stable code values

Code HTTP Meaning Retry safe?
ssrf_blocked n/a Target IP in a private or reserved range (RFC 5735, RFC 6598, IPv6 reserved) No, change the URL
upstream_non_json varies Upstream returned a body that wasn't valid JSON Maybe, investigate
output_validation_failed n/a The MCP server's outputSchema rejected the upstream response (server bug or unexpected upstream shape) Maybe, report
bad_request 400 Input shape rejected by the FourA API No, fix the arguments
auth_failed 401 The FourA API key is missing, invalid, or deactivated; this is not about target-site credentials No, fix the FourA key
forbidden 403 Target rejected the request (anti-bot, geo-block) No, or switch to foura_proxy
not_found 404 Target URL or endpoint doesn't exist No
rate_limited 429 Per-key RPM cap hit Yes, wait retryAfter seconds
at_capacity 503 Concurrency cap hit (current.concurrency > limits.maxConcurrency) Yes, wait retryAfter seconds
service_disabled 503 Service disabled for your account (plan or maintenance) Contact support
service_unavailable 503 Generic 503 from upstream Yes, short backoff
upstream_error 500+ Upstream 5xx Yes, exponential backoff
upstream_client_error 4xx Other 4xx not covered above Usually no
upstream_unknown other Defensive, should not occur in practice Investigate
no_eligible_proxy n/a No proxy matches the strict exitCountries allowlist; details.exitCountries contains the normalized scope Retry later; change scope only explicitly

HTTP-level errors from the MCP server

Some failures happen at the MCP transport layer, before any tool is called. These return raw JSON-RPC errors (no structuredContent):

HTTP When What you see
400 Unsupported MCP-Protocol-Version header Unsupported MCP-Protocol-Version: <value>. Supported: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07.
401 Missing or malformed Authorization header JSON-RPC error + WWW-Authenticate: Bearer realm="foura-mcp", resource_metadata="https://foura.ai/docs/mcp/server#auth"
403 Disallowed Origin or Host header (DNS-rebinding defense, CVE-2025-66414) Origin <value> is not in the allowlist or Host <value> is not in the allowlist
405 GET or DELETE on /mcp (stateless mode) Method not allowed in stateless mode. Use POST /mcp.
413 Request body > 256 KB Express default 413

The allowlists for 403 are env-configurable for self-hosters via FOURA_MCP_ALLOWED_HOSTS and FOURA_MCP_ALLOWED_ORIGINS.

Refused browser profiles

A browser profile that the catalogue cannot present, or a profile sent with unblocker set to false, comes back as an upstream failure with the reason in error and the request never leaves FourA. The message names what is available, so retry with one of the listed combinations rather than the same one.

These are refusals, not outages: retrying the identical request cannot succeed, and no other browser was used in its place.

Retry strategy

Four buckets:

  • Wait and retry: rate_limited, at_capacity, service_unavailable, upstream_error. Honour retryAfter when present. Use exponential backoff with jitter when it is absent.
  • Preserve scope and retry later: no_eligible_proxy. Do not remove exitCountries or substitute another country silently. Change or widen the allowlist only when the user explicitly changes the requirement.
  • Do not retry until the input or credential is fixed: bad_request, auth_failed, not_found, ssrf_blocked. For auth_failed, verify the FourA API key, not target-site credentials.
  • Switch tool when the content requires it: forbidden on foura_single can justify a bounded foura_proxy attempt. Use foura_browser when the desired content needs JavaScript. After a successful proxy selection, pass the returned proxy ID into foura_browser.proxy instead of starting a new selection.

Retry example (TypeScript, MCP-side)

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");
}
  • MCP Server, the four tools and their schemas
  • MCP Recipes, workflow prompts shipped with the server
  • API Errors, same envelope at the underlying REST API layer
Last updated: August 6, 2026