Ошибки подключения MCP
Начните с трёх проверок: правильный ли адрес скопирован из Napulse, тот ли проект выбран при подключении и есть ли нужное действие в tools/list.
Подключение и HTTP
| Симптом | Причина и действие |
|---|---|
| «Внешнее подключение ещё не настроено» | Сервер не объявляет корректный публичный MCP-адрес. Настройку должен выполнить администратор установки |
HTTP 401, invalid or revoked API key |
Нет Bearer-заголовка, неверный/отозванный ключ, истёк OAuth-токен или сотрудник потерял доступ. Проверьте настройки авторизации и подключитесь заново при необходимости |
Скрипт или своя интеграция с токеном из входа через браузер через 5 минут получает 401 |
Так и задумано: токен входа через браузер живёт 5 минут. ИИ-приложение обновляет его само (до 30 дней с подключения), а скрипт этого не делает. Для скриптов создайте персональный ключ в Интеграции → API: он не истекает и отзывается там же. Подробнее — «Сколько живёт вход через браузер» |
HTTP 403 при браузерном запросе |
В том числе возможен запрещённый Origin. Не отключайте защиту: администратор должен проверить разрешённые источники и права |
HTTP 403, impersonation_forbidden, «В режиме входа под другим сотрудником создавать подключения нельзя» |
Ключ или OAuth-подключение пытаются создать в режиме «войти как». Нажмите «Вернуться к себе»; подключение создаёт сам сотрудник. Отключать подключения в этом режиме можно |
HTTP 503, «MCP выключен в проекте или на платформе» |
Подключения ИИ остановлены выключателем. Ключи и OAuth-подключения сохранены. Попросите сотрудника с правом «Управлять подключениями ИИ» включить MCP в блоке «MCP в проекте» (Интеграции → MCP). Если на странице написано «MCP выключен на платформе», включить его может только администратор платформы |
HTTP 403, mcp_disabled |
То же: MCP выключен в проекте или на платформе. Ответ приходит при обмене ключа или на уже открытое подключение. Список подключений и их отключение работают |
HTTP 403 при создании ключа или на странице MCP, без кода impersonation_forbidden |
У сотрудника нет права «Подключать ИИ (MCP)». Право «Технический блок» само его больше не даёт. Руководитель выдаёт право в настройках структуры |
HTTP 405 при открытии /mcp в адресной строке |
Браузер выполняет GET. API ожидает POST; это не страница интерфейса |
HTTP 406 |
Проверьте Accept: application/json, text/event-stream |
HTTP 429, temporarily_unavailable на адресах /api/mcp/oauth/… |
Исчерпан лимит OAuth для вашего адреса, вашей сети, этого подключения или (на экране согласия) для вас как сотрудника. Подождите столько секунд, сколько указано в заголовке Retry-After, и повторите один раз. Подробнее — в разделе «Лимиты OAuth» ниже |
HTTP 502, CRM backend is unavailable |
MCP не может обратиться к серверу CRM. Нужна проверка доступности серверных компонентов |
HTTP 503 |
Публичное подключение не включено или его конфигурация недоступна. Ключ сам по себе не устраняет эту причину |
Адрес localhost не работает во внешнем облачном приложении |
Этот адрес доступен только на машине, где запущен сервер. Облачному приложению нужен доступный ему публичный адрес установки |
Инструменты и данные
Ошибки действий могут приходить при HTTP 200 внутри result.isError: true. Причина находится в result.content[].text; также проверяйте JSON-RPC error.
| Ошибка или ситуация | Что проверить |
|---|---|
Инструмента нет в tools/list |
Версию сервера, scope подключения и реальные права сотрудника на действие. Добавление права в CRM не добавляет отсутствующий scope старому подключению |
scope_missing, monolith_403, отказ в доступе |
Права могли измениться после загрузки списка. Обновите каталог; проверьте роль, видимость объекта и разрешения подключения |
unexpected_arguments |
Уберите поля, которых нет в inputSchema, в том числе попытку передать другой projectId |
clients_read_required у paid_customers_report |
Список диалогов с именами доступен, как find_conversations: подключению нужно разрешение clients:read, сотруднику — право видеть диалоги. Без списка (includeConversations не задан) отчёт отдаёт числа |
acting_as_paused, acting_as_changed у get_bulk_job |
Задача массовых тегов остановлена: подключение в паузе «войти как» (CRM больше не пускает) или действует уже не от имени автора задачи. Проверьте get_acting_as_user и запустите задачу заново |
| Ошибка валидации аргументов | Проверьте типы, обязательные поля, ограничения строк, формат ID и даты по inputSchema |
not_found / объект не найден |
ID неверен, объект удалён либо недоступен в этом проекте. Получите ID через соответствующий список доступных объектов |
conflict при изменении статьи или доски |
Ваша expectedRevision устарела. Перечитайте объект и согласуйте правку с новой версией |
| Статья не редактируется | readOnly: true означает, что изменение недоступно этому подключению: статья может быть общей для рабочего пространства либо у сотрудника нет права управления базой знаний |
| Изменение графа робота требует редактора CRM | В схеме есть приватный AI-ключ, который MCP не возвращает. Правьте граф в CRM, чтобы не потерять скрытую настройку |
get_robot_step_stats не принимает stepId |
Нужен id узла из get_robot (config.nodes[].id) или ключ из robot_stats: латинские буквы, цифры, _ и -, до 128 символов. Узел с другим id (например, кириллическим) через MCP не читается — смотрите его статистику в редакторе CRM |
После вызовов get_robot_step_stats робот во VK отвечает клиентам с задержкой |
Сервис роботов на каждый такой вызов запрашивает в VK профили всех людей на шаге и после него, через тот же лимит канала, что и ответы робота. Попросите ИИ начинать с robot_stats и не перебирать шаги подряд, особенно первые (START) |
robots_request_failed или HTTP 502 у list_robot_versions / get_robot_step_stats |
Робот не найден в проекте или не виден сотруднику («свои» роботы), либо сервис роботов ответил неожиданно. Возьмите UUID из list_robots; включить версию через MCP нельзя, только в редакторе CRM |
rate_limited |
Слишком частые вызовы инструментов по этому ключу. В ответе сказано, какой бюджет исчерпан (читающих или изменяющих вызовов) и через сколько секунд повторить (retryAfterSeconds). Подождите это время, уменьшите параллелизм; не запускайте бесконечный повтор. Бюджеты чтения и изменений раздельные, а один вызов инструмента — одна единица, сколько бы диалогов он ни обработал |
mcp_audit_unavailable |
CRM не смогла сохранить запись журнала, поэтому изменяющее действие не выполнено. Повторите позже; если ошибка не проходит, сообщите администратору установки |
| Задачи не видны на выбранной доске | create_task создаёт стандартную клиентскую задачу; у пользовательских досок отдельные инструменты |
smm_post_not_found у get_smm_post |
Пост в другом проекте или удалён из базы. Ответ одинаковый для обоих случаев. Возьмите ID из list_smm_posts |
invalid_cursor у list_smm_posts |
afterId не является постом этого проекта. Передавайте nextCursor из предыдущего ответа без изменений |
period_too_long / invalid_period у SMM |
Окно get_smm_schedule длиннее 92 дней либо to не позже from. Разбейте период на части |
conversation_not_found, feedback_not_found, pending_not_found у кураторских инструментов |
Диалог, разбор или удержанный ход в другом проекте, удалён, уже решён либо скрыт от сотрудника. Ответ одинаковый для всех случаев. Возьмите ID из review_queue или list_pending_validations |
feedback_conversation_unknown (HTTP 409) у validate_pending |
У удержанного хода нет диалога в CRM, поэтому записать критику нельзя. Ход не отправлен. Повторите решение без critique |
validation_resolve_failed или robots_unavailable (HTTP 502) у validate_pending |
Сервис роботов не принял решение: пока robots-clj не подключил доставку, approve, edit и replace отвечают так всегда. Разбор не записан, сообщение не отправлено |
subscriber_group_not_found (HTTP 404) у create_broadcast_draft |
Группа подписчиков из другого проекта, удалена или id неверен. Возьмите id группы на экране «Группы подписчиков» или через list_subscriber_groups |
broadcast_not_found (HTTP 404) у schedule_broadcast |
Рассылка в другом проекте, удалена или создана другим сотрудником, а у вас доступ «только свои». Ответ одинаковый для всех случаев. Возьмите id из list_broadcasts |
broadcast_not_editable (HTTP 409) у schedule_broadcast |
Рассылка уже отправляется или отправлена. Расписание меняется только у черновика или запланированной рассылки |
scheduled_at_in_past (HTTP 400) |
Время отправки уже прошло. Передайте будущее время в ISO 8601 с часовым поясом, например 2026-10-20T10:00:00+03:00 |
broadcast_template_unsupported (HTTP 400) у create_broadcast_draft |
В тексте переменная, которую CRM не подставит ({Имя}, {{firstName}}, %username%), пустой вариант ({привет|}), вложенная или непарная фигурная скобка, или текст, который у человека без данных станет пустым (например, только {firstName}). Доступны {firstName}, {lastName}, {name}, {username}, {phoneNumber}, {email}; запасное слово — {firstName|друг}, случайный вариант — {привет|добрый день}. Эти правила написаны и в описании самого инструмента |
client_request_conflict (HTTP 409) у create_broadcast_draft |
Этот clientRequestId уже использован для другого черновика. Для нового черновика нужен новый UUID |
Can only start unsent draft or scheduled broadcasts у run_broadcast |
Рассылка уже запущена или отправлена. Повторный запуск не выполняется; проверьте статус в list_broadcasts |
Короткая страница review_queue или search_feedback |
Записи по скрытым от сотрудника диалогам отсекаются после выборки. Продолжайте со следующим offset |
receipt_not_found у чеков |
Чек в другом проекте, удалён или скрыт от сотрудника настройками отдела продаж. Ответ одинаковый для всех случаев. Возьмите ID из list_receipts |
receipt_already_linked (HTTP 409) у link_receipt_to_deal |
У чека уже есть оплата по другой сделке: один чек — одна оплата. Повтор с той же сделкой не ошибка — вернётся уже созданная оплата. Перенос оплаты делается в CRM |
contact_mismatch, deal_closed (HTTP 409) у link_receipt_to_deal |
Сделка другого клиента или уже проиграна/в архиве. Найдите открытую или выигранную сделку клиента чека через list_deals с contactId |
receipt_rejected, receipt_not_detected, receipt_amount_unknown, receipt_currency_unknown (HTTP 409) |
Отклонённый чек, «не чек» или чек без суммы/валюты привязать нельзя. Сумму и валюту исправляют в CRM |
amount_exceeds_receipt |
Сумма оплаты больше суммы чека. Уменьшите amount или не передавайте его — тогда возьмётся сумма чека |
invalid_cursor у list_receipts |
Курсор повреждён или взят не из nextCursor. Начните с первой страницы |
Пустая страница задач/сделок при наличии nextCursor |
Продолжайте пагинацию через afterId: фильтрация видимости может дать пустую промежуточную страницу |
Лимиты по умолчанию — на каждый ключ или вход через браузер, два отдельных бюджета:
- чтение — пополнение 120 вызовов в минуту, кратковременный запас 30;
- изменения (инструменты, которые что-то меняют или отправляют) — 30 вызовов в минуту, запас 10.
Исчерпанные изменения не мешают чтению. Массовый инструмент считается одним вызовом: порции, которыми CRM обрабатывает диалоги внутри, бюджет не тратят. Администратор установки может изменить значения переменными MCP_RATE_LIMIT_READ_PER_MINUTE и MCP_RATE_LIMIT_READ_BURST (чтение), MCP_RATE_LIMIT_PER_MINUTE и MCP_RATE_LIMIT_BURST (изменения). Проверки инициализации и каталога не являются вызовами бизнес-инструментов.
Подключения и журнал на странице MCP
Список подключений и журнал вызовов — на странице Интеграции → MCP (или API), блок «Подключения и журнал». Подробнее — в разделе «Где видно подключения».
| Ситуация | Что проверить |
|---|---|
| Подключение есть в приложении, но его нет в «Мои подключения» | Открыт другой проект. Подключение относится к одному проекту; переключите проект в меню. Отключённые подключения видны с флажком «Показать отключённые» |
| «Ещё не использовался» у подключения, которым пользуются | Приложение ещё ни разу не вызвало инструмент: проверьте адрес и авторизацию. Время обновляется не чаще раза в минуту, поэтому только что сделанный вызов может появиться с задержкой до минуты |
| «Последнее использование» обновляется, а действия не выполняются | Обращение было, но CRM его отклонила: откройте «Журнал вызовов» с флажком «Только ошибки и отказы». «Отказано: нет доступа» — у сотрудника нет права на объект или разрешения у подключения; MCP могли выключить в проекте |
| «Вход через браузер истёк или отозван приложением» | Семейство OAuth-токенов живёт до 30 дней или приложение само вышло. Подключите приложение заново; старое подключение можно отключить |
| Нет вкладки «Все подключения проекта» и фильтра по сотруднику | Нужно право «Управлять подключениями ИИ». Без него видны только свои подключения и свой журнал |
| В журнале нет текста сообщения или аргументов | Так задумано: экран показывает только метаданные (действие, объект, результат). Тексты для клиентов и аргументы на экран не выводятся |
| В журнале строка «Инструмент «…»» или «Запрос GET /…» | Действие новое и ещё не получило подписи. Это не ошибка; запись полная |
Запрос отправки завис или вернулся с предупреждением
- Не повторяйте отправку автоматически.
- Если в ответе есть
delivery: "sent", провайдер подтвердил отправку. Предупреждение может относиться к дальнейшему сохранению результата в CRM. - Проверьте историю выбранного диалога и фактическую доставку доступным способом.
- Повторяйте действие только после выяснения, было ли оно выполнено.
Аналогичный подход нужен после тайм-аута создания статьи, приглашения, задачи или запуска рассылки. Одинаковый JSON-RPC id служит сопоставлению ответа и запроса, а не гарантией защиты от повторного выполнения.
Ошибки OAuth
- Запрос согласия истёк: начните подключение заново во внешнем приложении.
- Адрес возврата не совпадает: разработчик клиента должен использовать точно зарегистрированный redirect URI.
503 temporarily_unavailableна обновлении токена без заголовкаRetry-After: MCP выключен в проекте или на платформе. Подключение не удаляется; после включения MCP приложение продолжит работу само.invalid_grant: код или refresh token могли истечь, быть уже использованы или отозваны. Для пользователя решение — повторное подключение; для разработчика — проверка PKCE и ротации токенов.invalid_target: значение OAuthresourceдолжно соответствовать адресу подключаемого/mcp.invalid_scopeна обновлении токена: приложение попросило разрешение, которого не было в подключении, или у сотрудника не осталось ни одного из разрешений этого подключения. Если у сотрудника просто стало меньше прав, обновление проходит: новый токен получает только оставшиеся разрешения (полеscopeв ответе), подключение продолжает работать. Если пропали все разрешения подключения, подключите ИИ заново с другими разрешениями или верните сотруднику права.- После обновления токена нельзя повторно использовать старый refresh token: это отзывает семейство подключения.
Лимиты OAuth (HTTP 429)
Адреса подключения (register, authorize, token, revoke) открыты без входа, поэтому у них есть лимиты. Решение на экране согласия (consent) требует входа в CRM и считается по сотруднику. Они считаются для каждого клиента отдельно: один чужой адрес или одна чужая сеть IPv6 не может закрыть подключение остальным. Поверх них есть общие потолки на всю установку, которые защищают базу от атаки с многих адресов сразу.
| Что ограничено | Лимит | Кто упирается на практике |
|---|---|---|
Регистрация нового подключения (register) |
100 в час с одного адреса | Скрипт или клиент, который регистрируется заново при каждой попытке. Пользователи одного облачного ИИ-приложения (Claude, ChatGPT) делят его общие адреса и этот лимит |
Открытие экрана согласия (authorize) |
120 в час с одного адреса | Зацикленное перенаправление в браузере |
Обновление и отзыв токенов (token, revoke) |
600 выданных токенов в час на одно подключение (client_id), 600 неудачных попыток в час на одно подключение с одного адреса и 30 000 запросов в час с одного адреса (около 2500 подключений облачного ИИ-приложения за одним адресом) |
Клиент, который обновляет токен на каждый запрос (лимит выданных), повторяет ошибку без паузы (лимит неудач) или перебирает много client_id с одного адреса (лимит адреса) |
Решение на экране согласия («Разрешить» или «Отказать», consent) |
60 в час на одного сотрудника | Только скрипт, работающий с сессией сотрудника. Человек нажимает кнопку несколько раз за подключение |
| Всё перечисленное с одной сети IPv6 /48 (адреса IPv6 одного провайдера, компании или сервера) | 100 регистраций, 300 экранов согласия и 30 000 обновлений токенов в час на всю сеть | Скрипт, который перебирает адреса IPv6 внутри своей сети |
| Регистрации, экраны согласия и обновление токенов по всей установке | 3000, 3000 и 500 000 в час | Только при атаке сразу с десятков адресов или сетей (от 17 адресов IPv4 или от 10 сетей IPv6 /48); обычная нагрузка до этого не доходит |
Обычное подключение обновляет токен примерно 12 раз в час (токен действует 5 минут), то есть расходует 2% своего лимита.
Лимит подключения (token:client в журнале сервера) расходуют только успешные обмен кода и обновление токена. client_id не секрет: ИИ-приложение отправляет его в каждом запросе. Неудачные попытки (чужой или выдуманный refresh token, неверный код) этот лимит не расходуют. Поэтому посторонний, узнавший client_id, не может заблокировать обновление токена чужому подключению.
Неудачные попытки с ответом 4xx считаются отдельно, по паре «подключение + адрес» (token:client-address-failures в журнале сервера, 600 в час). Пример: после отзыва ключа ИИ-клиент раз за разом шлёт мёртвый refresh token. После 600 неудач он получает 429 до конца часа, и дальше его попытки не расходуют лимит адреса. Остальные подключения того же облачного ИИ-приложения за тем же общим адресом продолжают работать. Посторонний, который повторяет чужой client_id со своего адреса, расходует только эту пару для своего адреса. Ошибки сервера (500, 503) не считаются. Перебор токенов с разными client_id упирается в лимит своего адреса. Отзыв (revoke) лимиты подключения не расходует и ими не ограничивается.
Лимит экрана согласия (consent:user) считается по сотруднику, а не по адресу: коллеги за одним офисным NAT его не делят. Запросы с чужой страницы (другой Origin) отклоняются раньше и лимит не расходуют. Экран показывает «Слишком много попыток подключения за последний час».
Облачные ИИ-приложения ходят к Napulse за всеми своими пользователями с нескольких общих адресов. Если один их пользователь подключает Napulse больше 100 раз за час, новые подключения остальных пользователей этого приложения с того же адреса получают 429 до конца часа (register:ip в журнале сервера). Уже подключённые продолжают работать.
Ответ при превышении: HTTP 429, тело {"error":"temporarily_unavailable"}, заголовок Retry-After — через сколько секунд счётчик обнулится (не больше часа). Что делать:
- Пользователю: подождите указанное время и подключитесь заново. Не нажимайте «Подключить» и «Разрешить подключение» много раз подряд: каждая попытка расходует лимит.
- Разработчику клиента: соблюдайте
Retry-After, не повторяйте запрос в цикле, обновляйте токен только когда он истёк. - Администратору: если
429одновременно получают все клиенты, посмотрите в журнале сервера строкиMCP OAuth rate limit reached: <лимит>. Лимит с окончанием:global(register:global,authorize:global,token:global) — сработал общий потолок: идёт атака с десятков адресов или сетей сразу, и этот шаг подключения закрыт для всех до конца часа или до перезапуска сервера. Лимит с окончанием:net48— много запросов из одной сети IPv6 /48; он закрывает только эту сеть, остальные клиенты работают.token:clientзакрывает одно подключение, которое за час получило 600 токенов (зацикленный клиент);token:client-address-failuresзакрывает одно подключение с одного адреса после 600 неудачных попыток за час (клиент повторяет мёртвый refresh token: переподключите ИИ-приложение);consent:user— одного сотрудника. Если в строках только лимиты:ip, значит сервер не видит настоящие адреса. Проверьте переменнуюMCP_TRUSTED_PROXY_CIDRS: адрес обратного прокси (kamal-proxy) должен входить в список доверенных. Если переменная не задана, доверенными считаются локальные и частные сети. СтрокаMCP_TRUSTED_PROXY_CIDRS: ignored invalid entriesв журнале означает опечатку в списке.
Счётчики хранятся в памяти процесса и обнуляются при перезапуске сервера.
Не путайте с ошибкой rate_limited в ответе инструмента: это отдельный лимит на частоту вызовов инструментов (см. таблицу выше).
Мониторинг отказов (для администратора платформы)
Отказы подключений ИИ видны в трёх местах. Журнал MCP (mcp_audit_log) пишет только запросы действующих подключений, а мониторинг считает все отказы: запросы без ключа, с отозванным ключом, с чужого сайта, с истёкшей сессией, ошибки OAuth.
Карточка. Админка → Мониторинг → «MCP: отказы за час / сутки». Видна только администратору платформы. На ней:
- число отказов за час и за сутки;
- причины за выбранное окно («Час» или «Сутки»): где отказали, причина, HTTP-статус, количество;
- последние 20 отказов: время (UTC), причина, инструмент или шаблон маршрута;
- сводка журнала MCP за сутки: сколько вызовов, сколько отказов, частые отказы (инструмент или маршрут и код ошибки) и проекты с отказами.
Счётчики живут в памяти процесса CRM и обнуляются при перезапуске и деплое (время начала подсчёта написано в карточке). Долгую историю смотрите в Grafana (ниже). Данные карточки отдаёт GET /api/admin/mcp/ops?windowMinutes=60|1440: только администратор платформы, без аргументов, cookie, IP-адресов, ключей и текстов, Cache-Control: no-store. Подключение ИИ (mcp.sid) эту ручку прочитать не может.
Причины отказов. Список закрытый: другие значения в счётчики не попадают.
| Где | Причина | Что значит и что делать |
|---|---|---|
Адаптер /mcp |
missing_bearer |
Запрос без ключа. Обычно это первый шаг подключения ИИ-клиента (за ним идёт OAuth), сам по себе не тревожный |
Адаптер /mcp |
invalid_or_revoked |
Ключ или OAuth-токен недействителен или отозван. Всплеск — признак перебора ключей или клиента, который не заметил отзыв |
Адаптер /mcp |
origin_not_allowed |
Запрос из браузера с чужого сайта. Защиту не отключать |
Адаптер /mcp |
backend_unavailable (502), internal (500) |
Адаптер не достучался до CRM или упал. Проверить контейнер CRM и журнал сервера |
Адаптер /mcp |
rate_limited |
Лимит вызовов инструментов (ответ HTTP 200 с ошибкой внутри). Клиент вызывает инструменты слишком часто |
Адаптер /mcp |
scope_missing |
Вызван инструмент каталога, которого нет у подключения: изменились права или разрешения |
Адаптер /mcp |
monolith_401, monolith_403 |
CRM отказала инструменту: сессия не принята или нет прав на объект |
| Адаптер, охранник | mcp_disabled |
MCP выключен в проекте или на платформе. Это пауза, а не ошибка |
Охранник mcp.sid |
guard_no_secret |
Запрос с mcp.sid пришёл не от адаптера (без секрета внутреннего канала). Всплеск после деплоя — сломан секрет; без деплоя — попытка обойти адаптер |
| Охранник | guard_bad_cookie |
Повреждённая, двойная или смешанная с обычным входом cookie mcp.sid |
| Охранник | guard_session_missing |
Сессия mcp.sid истекла (живёт 5 минут) или не найдена |
| Охранник | guard_context_mismatch |
Ключ отозван или сессия принадлежит другому сотруднику или проекту |
| Охранник | guard_route_denied |
Адрес вне разрешений подключения (пишется и в журнал MCP) |
| Охранник | guard_conversation_denied |
Диалог в адресе скрыт от сотрудника |
| Охранник | guard_policy_error |
Не удалось проверить права (например, недоступна база прав). Запрос отклонён |
| Охранник | guard_audit_unavailable |
Журнал MCP недоступен, изменяющий запрос отклонён (503) |
| OAuth | rate_limited |
Сработал лимит OAuth (429), см. раздел «Лимиты OAuth» |
| OAuth | oauth_<код> |
Ошибка OAuth с этим кодом (oauth_invalid_grant, oauth_login_required…); неизвестный код — oauth_other, сбой сервера — oauth_server_error |
Журнал сервера. Каждый отказ адаптера и охранника — одна JSON-строка в журнале CRM (journald), без ключей, адресов и аргументов:
{"event":"mcp_refusal","at":"2026-10-17T10:00:00.000Z","surface":"guard","reason":"guard_route_denied","status":403,"route":"/api/conversations/:conversationId","method":"GET"}
Отказы OAuth построчно не пишутся (их видно в карточке и метрике; для лимитов OAuth остаются строки MCP OAuth rate limit reached).
Оповещение. Если за 10 минут набирается больше 50 отказов invalid_or_revoked + guard_route_denied, в ops-чат Telegram уходит сообщение [production] WARNING MCP refusals с числом отказов. Следующее — не раньше чем через час. Работает, только если заданы OPS_ALERT_TELEGRAM_BOT_TOKEN и OPS_ALERT_TELEGRAM_CHAT_ID (тот же чат, что у остальных оповещений). Что делать: открыть карточку, посмотреть причины, проекты с отказами и последние отказы; при подозрении на утечку — отозвать ключ на странице MCP проекта.
Метрика и Grafana. Счётчик OTel leadforgeai.mcp.event.count с атрибутами lf.layer=mcp, mcp.surface (transport, guard, oauth), mcp.reason, http.response.status_code, mcp.tool (только имя инструмента каталога). После перевода OTLP → Prometheus он, скорее всего, называется leadforgeai_mcp_event_count_total.
Панель на уже существующей метрике HTTP-запросов (работает и без новой метрики, но не видит отказов внутри ответа 200):
sum by (http_route, http_response_status_code) (
increase(leadforgeai_api_server_request_count_total{
http_route=~"/mcp|/api/mcp/.+|/\\.well-known/oauth-.+",
http_response_status_code=~"401|403|429|503"
}[24h])
)
Панель по новой метрике:
sum by (mcp_surface, mcp_reason) (increase(leadforgeai_mcp_event_count_total[24h]))
Имена метрик и меток после перевода OTLP → Prometheus не проверены на проде. Проверьте на хосте перед созданием панели:
curl -s 'http://127.0.0.1:19090/api/v1/label/__name__/values' | tr ',' '\n' | grep -E 'leadforgeai_(api_server_request_count|mcp_event_count)'
Если имя другое, поправьте его в запросах.
Что передать поддержке
Укажите время ошибки, название проекта, название инструмента, HTTP-статус и текст ошибки без секретов. Приложите минимальный набор аргументов, скрыв персональные сведения, если они не нужны для диагностики. Не присылайте API-ключ, OAuth-токены, cookies и полные заголовки авторизации.