Ошибки сервера MCP
Ошибки MCP Server
Как обрабатывать ошибки, возвращаемые foura-mcp server.
Каждый ответ с ошибкой от любого из четырех инструментов (foura_auto, foura_single, foura_proxy, foura_browser) является структурированным. LLM-агенты могут считывать поле code для логики повторных попыток без парсинга обычного текста.
Структура envelope
Каждая ошибка (isError: true) содержит блок structuredContent. Минимальный набор полей для каждой ошибки:
{
"service": "auto | single | proxy | browser",
"code": "rate_limited",
"error": "Rate limit exceeded"
}
При ошибках upstream с HTTP-статусом также присутствует status. Если вызов отклоняется общей квотой платформы, конверт содержит retryAfter, current.{concurrency, rpm} и limits.{maxConcurrency, maxRpm} в том же формате, что и базовые ошибки REST API.
Когда вызов отклоняется из-за лимита вашего тарифного плана, кодом ошибки становится сам лимит: plan_limit_ с последующим credits, bandwidth, rate, concurrency, browser_daily, premium или feature. Поле retryAfter содержит время ожидания, если ожидание снимает ограничение, и отсутствует для лимитов, которые ожиданием не снимаются (например, недоступная в тарифе функция). plan_limit_browser_daily также не содержит retryAfter: сброс происходит в полночь по UTC.
На foura_auto лимит плана, достигнутый внутри очереди, возвращается как rate_limited или forbidden с кодом тарифа в reason.
Фиксированные значения code
| Код | HTTP | Значение | Можно повторять? |
|---|---|---|---|
ssrf_blocked |
н/д | Целевой адрес является приватным или зарезервированным (RFC 5735, RFC 6598, зарезервированные IPv6), URL не использует http(s), или имя хоста не удалось разрешить | Нет, проверьте URL. Кратковременный сбой разрешения имени можно повторить |
upstream_non_json |
разный | Upstream вернул тело, которое не является валидным JSON | Возможно, требуется анализ |
output_validation_failed |
н/д | outputSchema на стороне MCP server отклонил ответ upstream, или инструмент не смог выполнить вызов (не настроен API key, API недоступен) |
Возможно: проверьте настройки, затем отправьте отчет |
bad_request |
400 | Формат входных данных отклонен FourA API | Нет, исправьте аргументы |
auth_failed |
401 | FourA API key отсутствует, недействителен или деактивирован; это не связано с учетными данными целевого сайта | Нет, исправьте FourA key |
forbidden |
403 | Целевой ресурс отклонил запрос (проверка сайта, ограничение по стране) | Нет, или переключитесь на foura_proxy |
not_found |
404 | Целевой URL или endpoint не существует | Нет |
rate_limited |
429 | Общий лимит платформы в минуту или 429 от целевого ресурса, отклоненный вашим validate. На foura_auto это также могут быть кредиты, трафик или rate limit вашего тарифа (см. reason) |
Да, подождите retryAfter при наличии, иначе используйте backoff |
at_capacity |
503 | Достигнут лимит параллельных запросов (current.concurrency > limits.maxConcurrency) |
Да, подождите retryAfter секунд |
service_disabled |
503 | Сервис отключен на обслуживание. Инструмент, не входящий в ваш тариф, возвращает plan_limit_feature |
Свяжитесь с поддержкой |
service_unavailable |
503 | Общий 503 от upstream | Да, короткий backoff |
upstream_error |
500+ или 0 | Целевой ресурс вернул ошибку сервера, либо на foura_proxy, foura_browser и foura_auto не ответили |
Да, экспоненциальный backoff |
upstream_client_error |
4xx | Другие 4xx, не указанные выше | Обычно нет |
upstream_unknown |
другое | Запрос выполнен, но не дал принятого ответа: на foura_single целевой ресурс не ответил (таймаут, отказ в соединении), а в любом инструменте ваш validate отклонил ответ 2xx или 3xx. Смотрите status и error |
Требуется анализ |
no_eligible_proxy |
н/д | Ни один proxy не соответствует строгому списку разрешений exitCountries; details.exitCountries содержит нормализованный scope |
Повторите позже; меняйте scope только явно |
plan_limit_credits |
429 | Ежемесячные кредиты вашего тарифа исчерпаны | Да, после retryAfter, или смените тариф |
plan_limit_bandwidth |
429 | Лимит трафика вашего тарифа исчерпан для этого расчетного периода | Да, после retryAfter, или смените тариф |
plan_limit_rate |
429 | Лимит запросов в минуту вашего тарифа для этого endpoint | Да, после retryAfter |
plan_limit_concurrency |
429 | Лимит параллельных запросов вашего тарифа для этого endpoint | Да, после retryAfter |
plan_limit_browser_daily |
429 | Дневной лимит Browser для вашего тарифа исчерпан | Да, завтра, или используйте foura_single / foura_proxy |
plan_limit_premium |
403 | Вы отправили exitClass: "premium" на тарифе, который не включает premium выходы |
Нет, уберите параметр или смените тариф |
plan_limit_feature |
403 | Тариф не включает этот endpoint или эту возможность | Нет, смените тариф |
Ошибки уровня HTTP от MCP-сервера
Некоторые сбои происходят на транспортном уровне MCP, до вызова какого-либо инструмента. Они возвращают необработанные ошибки JSON-RPC (без structuredContent):
| HTTP | Когда | Что вы видите |
|---|---|---|
| 400 | Неподдерживаемый заголовок MCP-Protocol-Version |
Unsupported MCP-Protocol-Version: <value>. Supported: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07. |
| 401 | Вызов инструмента или чтение ресурса без ключа API. Получение списка инструментов и промптов работает без него | Ошибка JSON-RPC + WWW-Authenticate: Bearer realm="foura-mcp" |
| 403 | Запрещенный заголовок Origin или Host (защита от DNS-rebinding, CVE-2025-66414) |
Origin <value> is not in the allowlist или Host <value> is not in the allowlist |
| 405 | GET или DELETE на /mcp (stateless-режим) |
Method not allowed in stateless mode. Use POST /mcp. |
| 413 | Тело запроса > 256 KB | Стандартная 413 от Express |
Разрешенные списки для 403 настраиваются через переменные окружения FOURA_MCP_ALLOWED_HOSTS и FOURA_MCP_ALLOWED_ORIGINS при самостоятельном хостинге.
Отклоненные профили браузера
Профиль браузера, который каталог не может предоставить, или профиль, переданный со значением false в unblocker, возвращается как сбой upstream с указанием причины в error, при этом запрос не покидает FourA. В сообщении перечислено, что доступно, поэтому повторите попытку с одной из указанных комбинаций вместо той же самой.
Это отказы, а не сбои в работе сервиса: повторение идентичного запроса не принесет успеха, и никакой другой браузер взамен использован не был.
Стратегия повторных попыток
Пять категорий:
- Отказ со стороны вашего тарифного плана, а не целевого ресурса: любой код
plan_limit_*. Та же операция через другой инструмент также будет отклонена, поэтому смена endpoint лишь тратит время. Дождитесь окончанияretryAfter, если он есть; в противном случае необходимо изменить тарифный план.plan_limit_premiumможно устранить самостоятельно, убравexitClass. - Подождать и повторить попытку:
rate_limited,at_capacity,service_unavailable,upstream_error. УчитывайтеretryAfterпри наличии. Используйте экспоненциальную задержку с джиттером (exponential backoff with jitter), если заголовок отсутствует. Не пытайтесь решить проблему одновременным повтором всех вызовов инструментов из очереди: вместо этого уменьшите количество параллельных запросов. - Сохранить область видимости и повторить позже:
no_eligible_proxy. Не удаляйтеexitCountriesи не подменяйте страну скрытно. Изменяйте или расширяйте список разрешенных только тогда, когда пользователь явно меняет требования. - Не повторять попытку до исправления входных данных или учетных данных:
bad_request,auth_failed,not_found,ssrf_blocked. Дляauth_failedпроверьте ключ API FourA, а не учетные данные целевого сайта. - Переключить инструмент, если этого требует контент:
forbiddenнаfoura_singleможет обосновать ограниченную попытку сfoura_proxy. Используйтеfoura_browser, когда для получения нужного контента требуется JavaScript. После успешного выбора прокси передайте возвращенный IDproxyвfoura_browser.proxyвместо запуска нового выбора.
Пример повторных попыток (TypeScript, со стороны 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");
}
Связанные разделы
- MCP Server: четыре инструмента и их схемы
- MCP Recipes: готовые промпты для воркфлоу, поставляемые с сервером
- API Errors: тот же формат ответа на базовом уровне REST API
- Rate Limits: лимиты аккаунта и платформы для
rate_limitedиat_capacity