Доступ и безопасность MCP | Napulse

Доступ и безопасность MCP

Подключение действует от имени сотрудника, который его создал или подтвердил. AI-приложение получает только пересечение четырёх условий:

выбранный проект ∩ текущие права сотрудника ∩ разрешения подключения ∩ доступ к конкретному объекту.

Права «Подключать ИИ» и «Управлять подключениями ИИ»

Право Что даёт Кто получил при обновлении
«Подключать ИИ (MCP)», mcp.connect Создать свой API-ключ, подтвердить OAuth-подключение Все, у кого было право «Технический блок» (tech_block.view)
«Управлять подключениями ИИ», mcp.manage Видеть и отключать подключения всех сотрудников проекта, выключать MCP в проекте Все, у кого было право «Управление» (management.manage)

Владелец проекта, участники корневого отдела и администратор платформы имеют оба права всегда («все права»). Право «Технический блок» само по себе больше не открывает MCP.

Право mcp.manage не расширяет видимость данных. Скрытые от сотрудника диалоги, сделки и задачи остаются скрытыми и для его подключений. Видимость «всё в проекте» по-прежнему есть только у владельца проекта и у тех, кому её дают права CRM (например, «Клиенты: смотреть все»).

Сотрудник с только техническими правами (без прав на чаты и управление) не видит диалоги клиентов в CRM. Поэтому и его подключение не получает разрешений clients:read, clients:reply и кураторских разрешений.

Если у сотрудника стало меньше прав (в том числе после этого обновления у сотрудника с только техническими правами), его подключения не обрываются, а сужаются до того, что ему ещё разрешено. Каждый вызов ИИ заново сверяется с текущими правами. Обновление OAuth-токена выдаёт новый токен только с оставшимися разрешениями и сообщает их в поле scope. Переподключать ИИ не нужно: роботы, задачи, аналитика и другие разрешённые инструменты продолжают работать. Разрешения, на которые сотрудник дал согласие при подключении, остаются за подключением. Если права вернут, вернутся и инструменты, без нового подключения. Исключение: ключ или подключение, у которого не осталось ни одного доступного разрешения (например, были только clients:* и кураторские), перестаёт работать (403 forbidden при обмене ключа, invalid_grant или invalid_scope при обновлении OAuth-токена). Такое подключение нужно создать заново с другими разрешениями или вернуть сотруднику права на чаты.

Выключатели MCP

Есть два аварийных выключателя:

  • в проекте — блок «MCP в проекте» на странице Интеграции → MCP, доступен сотруднику с правом mcp.manage;
  • на платформе — флаг «MCP: подключение внешнего ИИ» в админке платформы, доступен только администратору платформы. По умолчанию включён.

Выключатель останавливает все подключения ИИ (OAuth и API-ключи). Он не удаляет ключи и не останавливает экраны оператора, CRM-действия роботов, список подключений, их отключение и журнал.

Выключение действует сразу: сервер проверяет оба выключателя при каждом запросе подключения, а не только при входе. Ответы при выключенном MCP:

  • адрес /mcp — HTTP 503 с текстом «MCP выключен в проекте или на платформе», без повторного запроса OAuth;
  • обмен ключа и запросы уже открытого подключения — 403 с кодом mcp_disabled;
  • обновление OAuth-токена — 503 temporarily_unavailable: приложение сохранит подключение и сможет продолжить после включения.

Переключать MCP в проекте можно только из браузера под своей учётной записью. Внешний ИИ не может включить MCP сам себе, а в режиме «войти как» переключатель закрыт.

Один проект на подключение

Проект закрепляется при создании ключа или подтверждении OAuth. Если у сотрудника есть доступ к нескольким проектам, это не расширяет конкретное подключение. Для второго проекта понадобится отдельное.

Инструменты не принимают projectId и не позволяют менять пользователя, от имени которого выполняется действие. ID клиента, диалога, канала, сотрудника или другого объекта дополнительно проверяется сервером. Передача чужого ID не даёт доступа к чужому проекту.

Исключение по назначению данных — общие статьи базы знаний рабочего пространства, которые уже доступны сотруднику в CRM. MCP может прочитать их с отметкой readOnly: true, но не изменить через подключение проекта. Это не доступ ко всем статьям других проектов.

Разрешения не заменяют роль

Например, users:write разрешает использовать инструменты приглашения и назначения, но не отменяет иерархию управления. Руководитель не сможет назначить права выше собственных или управлять недоступной веткой. clients:reply не открывает скрытые диалоги. settings:write не даёт право менять настройки проекта сотруднику без требуемого уровня управления.

Чтение и запись выдаются отдельно. Создавайте подключение с минимальным набором под нужную задачу. Для поиска ответов в базе знаний достаточно knowledge:read; право изменять статьи добавляйте, когда оно действительно нужно.

Режим «войти как»

Администратор, который вошёл в CRM под другим сотрудником, не может создать подключение от его имени:

  • кнопки создания API-ключа нет, вместо неё показано объяснение;
  • на экране согласия OAuth кнопка «Разрешить подключение» выключена;
  • сервер отвечает 403 с кодом impersonation_forbidden, даже если запрос отправлен в обход экрана.

Причина: ключ и OAuth-подключение живут до отзыва, то есть дольше самого режима «войти как», и действовали бы от имени сотрудника без его участия. Подключение создаёт сам сотрудник под своей учётной записью.

В этом режиме по-прежнему можно смотреть список подключений, отключать их и отказывать в OAuth-запросе: эти действия доступ только закрывают.

Кураторские инструменты и экраны оператора

Кураторские инструменты (curator:feedback:*) не вводят нового права. Чтение разрешено тому, кто видит диалоги клиентов, запись — тому, кто ещё и может отвечать клиентам. Права проверяются заново при каждом запросе.

Каждый объект проверяется в самом обработчике:

  • карточка кейса и разбор: диалог должен быть в проекте подключения и виден сотруднику. Иначе 404 до чтения переписки и до вызова модели;
  • одобрение правила: запись ищется в проекте подключения, затем проверяется видимость её диалога. Чужая, отсутствующая и скрытая запись дают одинаковый 404;
  • решение по удержанному ходу: ход должен быть в проекте и виден. С критикой по ходу без диалога в CRM ответ 409 приходит до того, как ход уйдёт клиенту;
  • списки отсекают записи по скрытым диалогам.

Карточку кейса дополнительно проверяет охранник: адрес содержит номер диалога, и скрытый диалог отклоняется ещё до обработчика.

Экраны CRM «Очередь ревью» и «Режим оператора» работают через отдельный адрес /api/operator/feedback/* браузерной сессией. Этот адрес не принимает служебную сессию MCP и не входит в список адресов, доступных подключению. Экранам не нужно право подключать ИИ.

В журнале критика куратора хранится как длина и отпечаток SHA-256, как и текст ответа клиенту.

Ключи и OAuth-подключения без curator:feedback:* эти инструменты не получают. Чтобы ими пользоваться, создайте подключение заново с нужными разрешениями.

Чеки и оплаты

Разрешение receipts:read получает тот, кто видит чеки в CRM; receipts:write — тот, кто ещё и вправе вручную менять сделки. Права проверяются заново при каждом запросе.

  • Видимость. С правом «все чеки» видны все чеки проекта. С правом «чеки» — только чеки диалогов внутри границы отдела продаж, как на экране чеков. Граница считается по владельцу проекта, а не по праву «Управлять подключениями ИИ». Если границу не удалось посчитать, видны только чеки своих лидов.

  • Чего ответ не содержит никогда:

    • изображение чека — в адресе бывает токен бота Telegram или ключ VK, с которым можно управлять ботом;
    • сырые данные распознавания: рассуждения модели, получатель, назначение платежа, номер операции, счёт плательщика, банк — это платёжные персональные данные, которые для работы с воронкой не нужны;
    • заметки к чеку и стоимость распознавания;
    • внутренний идентификатор денег оплаты (cashReceiptId).

    CRM не читает эти колонки, когда отвечает подключению: случайно добавить их в ответ нельзя.

  • Привязка не делает выручку. link_receipt_to_deal создаёт только ожидающую оплату. Чек не помечается проверенным, сумма не подтверждается; в выручку оплата попадает только после подтверждения человеком. Один чек — одна оплата, параллельные вызовы не создают дубль.

  • Экранные адреса закрыты. Адреса экрана чеков (/api/receipts*) и оплат (/api/deal-payments) подключению недоступны: у MCP свои адреса /api/mcp/receipts* и /api/mcp/deal-payments с проверкой проекта, прав и видимости в каждом запросе.

В журнале запись запроса к карточке и привязке чека хранит номер чека (объект «чек»).

Рассылки

Чтение рассылок (broadcasts:read), черновики (broadcasts:write) и запуск с расписанием (broadcasts:send, у старых подключений — tasks:execute) подчиняются тем же правам, что экран «Рассылки». При доступе «только свои» подключение видит и меняет только рассылки, созданные этим сотрудником. Рассылка чужого проекта, чужого автора и несуществующая дают одинаковый ответ 404.

Что получает внешнее приложение:

  • в списке — название, статус, расписание, id автора, охват, первые 200 символов текста, число вложений и сводку аудитории: id групп подписчиков, канал, теги, число выбранных вручную контактов и признак «есть поиск»;
  • в статистике — только числа.

Не передаются: получатели и история доставки, полный текст, сами выбранные контакты, текст поиска по аудитории, служебный журнал отправки.

Черновик через ИИ никогда не отправляется сам. Отправка — отдельное действие run_broadcast, расписание — schedule_broadcast; оба требуют broadcasts:send (старое имя — tasks:execute) и права редактировать рассылки. run_broadcast отправляет сообщения реальным клиентам: давайте broadcasts:send только подключениям, которым это действительно нужно. Лимитов на число рассылок и минимального запаса времени нет (решение владельца 17.10).

В журнале каждое обращение к рассылке записывается с её номером (объект broadcast), текст черновика — как длина и отпечаток SHA-256.

Аудитории, правка и удаление черновиков (MCP v2, этап C3).

  • Новых разрешений нет: черновик, его правка и удаление — broadcasts:write; отмена расписания и пробная проверка отправки — broadcasts:send (до MCP v2 M3 — tasks:execute). Права — те же, что на экране «Рассылки» (право редактировать рассылки, «только свои» — только свои), чужой проект и чужой автор — одинаковый 404.
  • Аудитория по диалогам (список диалогов, фильтр диалогов, снимок, теги диалога) читает переписку и поэтому требует ещё clients:read у подключения и права видеть диалоги у сотрудника; состав — только из диалогов, которые сотрудник видит в CRM. Аудитория «по фильтру» хранится как фильтр «Лидов», «снимок» — как «конкретные контакты» экрана; оба вида при отправке ещё раз сужаются видимостью автора.
  • dryRun ничего не пишет: ни рассылки, ни аудитории; сервису роботов уходит только вопрос «кому можно писать». Удаляется только черновик, который ни разу не отправлялся (условие проверяется в самом удалении); отправленные рассылки и их история через MCP не удаляются.
  • В ответах по-прежнему нет телефонов, почты, username и id контактов: только число зафиксированных контактов и число ненайденных диалогов.
  • В журнале длинные списки аргументов (больше 100 id) хранятся как число, отпечаток SHA-256 и первые 5 id — полный список не помещался в запись журнала, и вызов оставался без записи.

Старые ключи и OAuth-подключения broadcasts:read и broadcasts:write сами не получают: создайте ключ заново или переподключитесь.

Сегменты и охват

find_conversations (clients:read) и preview_broadcast_audience (broadcasts:read) только читают: ничего не меняют, рассылку не создают, клиентам не пишут, роботов не запускают.

  • Видимость. Сегмент диалогов строится только по проекту подключения. Граница видимости отдела продаж («свои/все», по владельцу проекта, а не по праву управлять подключениями) накладывается после фильтров: фильтр сужает выдачу, но не расширяет её. Если границу не удалось посчитать, видны только свои диалоги. Сотрудник только технического блока сегментов не получает, как и списка диалогов в CRM.
  • Фильтры — механизмы CRM. Фильтры диалога — те же, что в панели «Клиенты», фильтры контакта — тот же построитель, что у «Лидов» и групп подписчиков. Неизвестный ключ в leadFilters отклоняется, как в аудитории рассылки. Отдельного построителя SQL у MCP нет; новые условия (давность, подтверждённая оплата, текст в сообщениях, место тега) приходят параметрами запроса, а не текстом SQL.
  • Персональные данные. В строке сегмента нет телефона, почты, username и служебной памяти диалога: только имя и фамилия контакта, как в карточке, и ответственный с той же настройкой скрытия имён. Фрагмент совпадения при поиске по тексту — не длиннее 160 символов; телефоны (от 10 цифр), номера карт и почта в нём заменены метками. Сам текст переписки остаётся данными клиента: его можно прочитать и через get_client_messages, фрагмент только экономит вызовы.
  • Оплаты. Фильтр «есть подтверждённая оплата» отвечает «да/нет» по клиенту и сумм не отдаёт. На экране «Клиенты» тот же сотрудник видит фильтры «Сделки» и «Сумма покупок», поэтому граница по деньгам (суммы — только под deals:read и аналитикой) не сдвигается.
  • Предпросмотр рассылки считается кодом самой отправки: видимость автора («только свои клиенты» оставляют только своих), аккаунты проекта, проверка сервиса роботов. Сервису роботов уходит только вопрос «кому из этих id можно писать» по каналам проекта — тот же, что при подсчёте охвата на экране «Рассылки»; кампания не создаётся. Нужно право редактировать рассылки, как на экране. Предпросмотр по диалогам дополнительно требует clients:read у подключения и права видеть диалоги у сотрудника: иначе фильтр по тексту сообщений работал бы как скрытый поиск по переписке под одним разрешением рассылок. Имена получателей и примеры текста — только сотруднику с правом видеть клиентов; в примерах телефон, почта и username заменены метками.
  • Журнал. Оба инструмента вызываются методом POST (фильтр и список диалогов идут в теле запроса), поэтому охранник считает их изменяющими: без сохранённой записи журнала запрос отклоняется с mcp_audit_unavailable. В журнал попадает шаблон адреса (/api/mcp/segments/…) без фильтра, текста поиска и списка диалогов; у записи инструмента — отпечаток аргументов, как у всех читающих инструментов.

Новых разрешений нет: инструменты сразу получают подключения, у которых уже есть clients:read или broadcasts:read.

Группы подписчиков: создание и правка

Разрешение groups:write (MCP v2) открывает create_subscriber_group, update_subscriber_group, delete_subscriber_group, recalculate_subscriber_group, add_to_subscriber_group и remove_from_subscriber_group. Старые подключения его не получают.

  • Права — как в CRM. Группу создаёт, меняет, удаляет и пересчитывает только тот, кто в CRM «редактирует всех клиентов»; состав снимка меняет и тот, кто «редактирует своих», — только у своих клиентов. Права пересчитываются на каждом запросе. Браузерные адреса CRM /api/subscriber-groups/* подключению по-прежнему закрыты; у MCP свои адреса /api/mcp/subscriber-groups/* с теми же проверками.
  • Подписные группы — это сайты. MCP их не меняет, не пересчитывает, не очищает и не удаляет.
  • Роботы. Пересчёт группы с включённым «запускать при попадании» запускает робота для каждого вошедшего, а робот может сразу написать клиенту. Поэтому триггер по умолчанию выключен, включается только явно, робот проверяется так же, как в редакторе группы (этот проект, сотрудник вправе его менять). При создании и правке условия робот не запускается никому. Подтверждения от сервера нет (решение владельца 18.10): dryRun показывает, скольких запустит робот, и описание инструмента велит ИИ сначала вызвать dryRun, если пользователь просил подтверждать.
  • Удаление только мягкое: контакты и их членство остаются. Используемую группу (рассылки, роботы, условия других групп, исключения, страница сайта) без force: true удалить нельзя — ответ называет, где она используется.
  • Данные. Пример в dryRun — не больше 10 строк: id контакта, имя и фамилия, площадка и id диалога, без телефона, почты и username. Варианты «по диалогам» требуют ещё clients:read: иначе фильтр по тексту сообщений стал бы скрытым поиском по переписке под разрешением групп. Чужой проект — «группа не найдена».
  • Журнал. Каждое действие, включая пробное, пишется в журнал MCP: охранник — адрес с объектом subscriber_group, адаптер — полные аргументы пишущего инструмента.

Эскалации ИИ

escalations:read (список) и escalations:write (взять себе, закрыть, вернуть роботу) выдаются по той же проверке, что пускает на экран «Эскалация» в CRM: просмотр или правка настроек ИИ, tech_block.manage или management.manage. Права пересчитываются на каждом запросе.

  • Видимость — строже экрана CRM. Экран эскалаций и его ручки видимость диалога не проверяют (записано в отчёт аудита). MCP проверяет задуманное: эскалация видна и доступна, только если её диалог сотрудник видит в CRM («свои/все»); иначе ответ тот же, что для несуществующей, — escalation_not_found. Если границу не удалось посчитать, видны только свои диалоги.
  • Как в CRM. Действия разрешены и с одним просмотром настроек ИИ — так устроен экран; MCP это повторяет. «Взять себе» назначает сотрудника ответственным тем же путём, что панель менеджера (открытые сделки, событие в ленте). «Вернуть роботу» снимает паузу в роботах тем же путём, что переключатель «Авто», — после этого робот снова пишет клиенту. Для проверки без последствий есть dryRun.
  • Персональные данные. В списке нет телефона, почты, username и служебной памяти диалога; заметка робота обрезается до 500 символов. Имена других менеджеров скрываются так же, как в карточке клиента.
  • Журнал. Действия — POST, без записи журнала охранник их отклоняет. В журнал попадает шаблон адреса и id эскалации, у пишущих инструментов — аргументы.

Массовые действия с диалогами

bulk_update_client_tags и get_bulk_job работают под отдельным разрешением clients:bulk. Массовые теги — такое же «опасное» действие, как рассылка: роботы реагируют на смену тегов (crm_tags_changed) и могут сразу написать клиентам. Поэтому разрешение отдельное, а старые подключения его сами не получают.

  • Кому выдаётся. Тому, кто может менять карточку клиента (правило clients:write) и делать массовые действия на экране «Лиды». В CRM массовые действия «Лидов» открыты только сотруднику, который редактирует контакты: так проверяют и экран, и сборка аудитории, и очередь роботов после перезапуска. MCP использует ту же проверку, а не свою. Право на рассылки (tech.broadcasts.*, ресурс «массовые действия» в настройках прав) — это экран «Рассылки», clients:bulk оно не даёт.
  • Граница видимости. Цель строится тем же кодом, что find_conversations: только проект подключения, граница отдела продаж («свои/все») после фильтров, сбой границы — только свои. Сотрудник без права «редактировать всех» контактов меняет только свои диалоги, даже если видит больше: так же сужается аудитория массовых действий «Лидов». Диалог из списка, которого сотрудник не видит, не меняется и считается «не найден» — без различия «нет» и «не видно».
  • Те же действия, что руками. Каждый диалог меняется общим действием CRM, которым пользуется панель и update_client_tags: блокировка строки, точная дельта, событие роботам по каждому изменённому диалогу. Новое имя тега попадает в каталог проекта тем же путём, что кнопка «создать тег», и только до изменения диалогов.
  • Подтверждения нет, есть пробный запуск. Сервер не держит кодов подтверждения (решение владельца 18.10). dryRun: true ничего не пишет — ни тегов, ни задачи, ни каталога — и показывает охват, пример и роботов, которые проверяют эти теги. Имена роботов показываются только тех, кого сотрудник видит в CRM; остальные — числом. Данные роботов читаются только на чтение и наружу уходят лишь id, название, состояние и совпавшие имена тегов, без графа.
  • Предела на число диалогов нет. Большой объём выполняется фоновой задачей по 200 диалогов. Перед каждой порцией права сотрудника и подключение проверяются заново: снятое право, отозванное подключение или выключенный MCP останавливают задачу. Задачу видит только тот, кто её запустил. В задаче хранятся цель (фильтр или список id) и имена тегов — без текстов клиентов; завершённые задачи удаляются через 30 дней.
  • Журнал. Вызов пишет запись инструмента и запись запроса, как обычно; длинный список диалогов в записи инструмента хранится как число и отпечаток. Завершение фоновой задачи пишет ещё одну запись с источником «Фоновая задача»: статус, счётчики и коды ошибок, без id диалогов и текстов. Массовый вызов в лимите вызовов считается за один.

SQL-консоль и тариф

  • SQL-консоль (sql). Открыта ровно тем, кому консоль открыта в CRM, той же функцией (canUseSqlConsole): оператор из ANALYTICS_SQL_OPERATOR_USER_IDS с просмотром всей аналитики и её правкой. Суперключи и владение проектом её не открывают. Запросы идут в адреса консоли CRM и выполняются её кодом: только чтение (запись, DDL и функции, выполняющие SQL из строки или читающие файлы, отклоняются), транзакция READ ONLY, таймаут 15 с, не больше 1000 строк, понижение роли SQL_CONSOLE_DB_ROLE, если она задана на сервере. Консоль работает по всей базе установки и фильтр персональных данных не применяет — как в CRM. В журнал MCP текст запроса не попадает: только длина, sha256 и число строк.
  • Тариф (billing:read, billing:write). Владельцу рабочего пространства проекта, которому виден раздел «Биллинг». Админ платформы, не владеющий пространством, тариф клиента через MCP не видит. Пополнение создаёт счёт в USDT: деньги не списываются, перевод делает человек. Служебные операции биллинга (ручная корректировка баланса, выключатель биллинга, каталог тарифов) через MCP недоступны.

Каналы и секреты

По решению владельца (18.10) ИИ может то же, что сотрудник в CRM руками, включая секреты каналов. Разрешения channels:read, channels:write, secrets:read выдаются только при тех же правах, что открывают экраны: «Аккаунты: смотреть / редактировать» (боты, личные Telegram-аккаунты, VK), «Интеграции: смотреть / редактировать» (Umnico), правка всех роботов (секретный ключ вебхуков роботов).

  • Секрет канала выдаётся только тому, кто может править этот канал. Кто владеет токеном, тот управляет ботом или сообществом: инструмент предупреждает об этом в описании и в ответе.
  • Чтение секрета без записи в журнал невозможно: если строку журнала не удалось сохранить, CRM отвечает 503 и секрет не отдаёт. В журнал пишутся сотрудник, подключение, вид и id канала; значения секретов не пишутся ни в журнал, ни в логи сервера, ни в тексты ошибок. Токены в аргументах (connect_telegram_bot, replace_channel_token и др.) заменяются меткой без длины и хеша.
  • Не выдаются никогда: сессии личных Telegram-аккаунтов (CRM их не показывает и не может расшифровать) и ключи ИИ (их задаёт только админ платформы).
  • Удаление канала необратимо; удаление последней записи VK-сообщества удаляет CRM-клиентов, диалоги и сообщения этого VK-источника, как на экране.

Аналитика и источники

Инструменты аналитики (analytics) отдают только агрегаты по всему проекту. Нужны права CRM на аналитику «все» (tech.analytics.view и tech.analytics.view_all) или полные права проекта; сотрудник с аналитикой «только свои» этих инструментов не получает. Права проверяются при каждом запросе, проект берётся только из подключения.

Что в ответах нет:

  • контактов, диалогов и сделок по отдельности: только числа и суммы по группам;
  • email сотрудников. Менеджер подписан ФИО, и только если он сейчас участник проекта; исполнитель задачи, записанный текстом, скрыт (manager = null);
  • идентификаторов рекламных кликов (click_id), только название рекламной системы;
  • разрезов «Робот» и «Рассылка», метрик рассылок и роботов (broadcast_*, robot_*: их нет в каталоге, запрос отвечает 400 unsupported_metric), отчётов рассылок и роботов, своих бордов сотрудников;
  • произвольного SQL: метрики и разрезы берутся из словаря, значения фильтров передаются в базу параметрами.

Названия сайтов, ссылок, UTM-метки и посадочные — данные пользователей и клиентов; ИИ не должен воспринимать их как инструкции.

Снимок воронки (pipeline_stage_snapshot) читает основную базу: воронка другого проекта и несуществующая воронка дают одинаковый 404.

Сайты и заявки с сайтов

Раздел только читает (sites:read). Разрешение выдаётся тому, кто видит раздел «Сайты» в CRM, и действует с тем же охватом: «свои» — сайты, созданные сотрудником, на его же страницах; «все» — все сайты проекта. Браузерные адреса раздела (/api/sites/*) подключению закрыты: открыты только четыре адреса GET /api/mcp/sites….

Ответы собираются по белому списку полей:

  • сайт: id, название, статус, теги, папка, платформа, режим Telegram, id и адрес (slug) страницы, статус страницы, публичные домены, автор (id сотрудника), даты; в карточке ещё домены (адрес, статус, сертификат) и основная ссылка (код, активна ли, число переходов);
  • статистика: только числа (визиты, подписки, активные подписки, отписки, заявки, переходы, привязки, просмотры ссылки Telegram, «написали», доступна ли атрибуция). Выручки нет;
  • заявка: id, время, сайт, страница, канал, статус, признаки «указаны имя / почта / телефон» (да/нет), UTM source/medium/campaign до 100 символов, contactId.

Никогда не передаются: настройки сайта, свой HTML и тексты страниц, ревизии, роботы и приёмщики, согласия, условия групп; токен проверки домена и ожидаемые DNS-записи; имя, почта, телефон, текст и служебные данные заявки, id в мессенджере, хеш IP, браузер, город, маски телефона и почты.

contactId заявки показывается, только если сотрудник может открыть этот контакт в CRM (та же проверка, что у карточки контакта). Иначе приходит contactId: null и contactHidden: true. Чужой и скрытый сайт дают одинаковый 404. В журнале запрос записывается с объектом «сайт» и его id.

Черновик ответа от ИИ

draft_client_reply (разрешение clients:write) ничего не отправляет клиенту: он кладёт черновик в окно ответа диалога, а решение принимает сотрудник.

  • Проект и диалог. Проект берётся из подключения, в аргументах его нет. Диалог ищется только в этом проекте и должен быть виден сотруднику по тем же правилам, что и на экране CRM (владелец проекта, назначения, линии; право «Управлять подключениями ИИ» видимость не расширяет). Охранник проверяет диалог ещё до обработчика. Чужой, скрытый и несуществующий диалог дают одинаковый 404.
  • Подпись. «Имя Фамилия (через ИИ)» ставит сервер; модель не может выдать черновик за другого сотрудника.
  • Что хранится. Таблица conversation_reply_drafts: проект, диалог, автор, подпись, источник (mcp или assistant), текст, статус (active, used, dismissed, replaced), кто и когда решил. Удаление диалога или проекта удаляет и его черновики.
  • Кому виден. Блок «Черновик от ИИ» видят те, кто видит диалог. Событие для открытого диалога (conversation:reply_draft_updated) несёт только номера и статус, без текста, и доставляется только тем, кому диалог виден.
  • Кто решает. «Вставить в ответ» и «Убрать» требуют права отвечать клиентам (clients.messages.send). Адреса этих кнопок (/api/conversations/{id}/reply-draft…) работают только с браузерной сессией: служебная сессия MCP там не принимается и в список адресов подключения они не входят, поэтому модель не может сама «вставить» или убрать черновик.
  • Журнал. Запись запроса хранит шаблон адреса и номер диалога, запись инструмента — текст черновика только как длину и отпечаток SHA-256.
  • Старые подключения. Ключи и OAuth-подключения, созданные до появления черновиков, clients:write не получают: нужно создать ключ заново или переподключиться.

Изменение прав и отзыв

Сервер повторно проверяет действующие права при запросах. После удаления сотрудника из проекта, отзыва подключения или ограничения прав последующие запросы лишаются соответствующего доступа. Уже открытый в AI-приложении каталог может потребовать обновления, но старое отображение инструмента не разрешает запрещённое действие.

Расширение прав работает иначе: подключение не получает разрешения, которых не было при его создании. Когда в каталоге появляется новое разрешение (например, чтение чеков), старые ключи и OAuth-подключения его не получают — нужно создать ключ заново или переподключиться и отметить разрешение на экране согласия.

Отзыв не отменяет ранее отправленное сообщение, созданную задачу или выполненную рассылку. Он также не удаляет копии сведений, уже полученные внешним приложением. Чтобы завершить работу, отключите подключение в Napulse и при необходимости удалите его из внешнего клиента.

Что передаётся внешнему приложению

При разрешении чтения приложение может получить содержимое доступных диалогов, статей, задач и других объектов. Поэтому разрешение на чтение — это реальная передача выбранных данных внешнему клиенту.

Группы подписчиков (groups:read) передаются без состава: только название, описание, режим, число клиентов и условие отбора. Кто входит в группу, MCP не отдаёт; текст поиска в условии заменяется отметкой hasSearch: true; робот группы и сведения о подписном сайте не передаются. Подробнее — в каталоге. Пишущие инструменты групп (groups:write) показывают в dryRun пример до 10 участников без контактных данных — см. выше.

С разрешением tasks:read приложение читает и комментарии к задачам, связанным с клиентом: текст, имя автора так, как оно записано в CRM, и id автора. Комментарии задач скрытых диалогов и других проектов не отдаются. Комментарий, добавленный через ИИ, подписан ФИО сотрудника (без e-mail), а в ленте клиента — «ФИО (через ИИ)»; в журнале его текст хранится как длина и отпечаток SHA-256.

SMM (разрешение smm:read, только чтение): приложение получает тексты постов, хештеги, статусы, время публикации, имя и фамилию автора, названия сообществ VK и каналов Telegram, ссылки на опубликованные посты и тексты ошибок площадок. Не получает: ссылки на медиафайлы, служебные id файлов Telegram, ботов, чатов и VK-сообществ, токены и почту автора. Из текста ошибки площадки перед выдачей вырезаются токены ботов и ключи доступа, длина ограничена 280 символами. Опубликовать, изменить, удалить или синхронизировать пост через MCP нельзя.

Статистика роботов отдаётся без клиентов: get_robot_step_stats возвращает только числа по шагу, группы ошибок и варианты A/B, без id, имён, телефонов и профилей людей на шаге. В тексте ошибок адреса, почты, токены и номера от 7 цифр заменяются метками. История версий робота (list_robot_versions) не содержит узлов графа, а автора называет ФИО сотрудника проекта, без почты.

У get_robot_step_stats есть побочные эффекты на стороне сервиса роботов, хотя инструмент только читает. Так сервис строит ответ и для редактора CRM:

  • у робота во VK каждый вызов запрашивает в VK профили всех клиентов, которые стоят на шаге или прошли его. Запросы идут пачками по 500 через лимит запросов канала — тот же, через который робот отвечает клиентам. На большом шаге (первые шаги, особенно START, проходят все клиенты) частые вызовы могут задержать ответы робота;
  • полученные профили сохраняются в базе роботов;
  • у A/B-узла счётчики вариантов пересчитываются и сохраняются.

MCP эти профили наружу не отдаёт. Описание инструмента сообщает об эффектах внешнему ИИ и советует начинать с robot_stats: он только читает и в VK не ходит. Отдельного лимита MCP для этого вызова нет (решение владельца 1). Чтение статистики шага без запросов к VK и без пересчёта — открытая задача сервиса роботов.

Токены подключённых каналов, приватные AI-ключи, секретные ссылки приглашений сотрудников и служебные секреты не входят в каталог MCP. Служебная память диалога (contextMemory) через MCP не отдаётся никогда: в ней хранятся данные передачи между линиями и имя ответственного, скрытое настройкой «Показывать имена других менеджеров». Карточка клиента (get_client_card) собирается по белому списку полей: контакт, пользовательские поля и источник лида — только при праве CRM на просмотр этой записи контакта, телефон и имя пользователя Telegram — по тем же правам видимости Telegram, что в панели; аватар и сырые поля контакта не отдаются. Запись карточки клиента (clients:write) не расширяет права сотрудника: теги, заметки и отметку «прочитано» меняет тот, кто видит диалог и отвечает клиентам, редактирует контакты или имеет управленческое право; ответственного — только сотрудник с управленческим правом, как в панели. Смена тегов — действие с побочным эффектом: робот может написать клиенту. Передача на линию (transfer_client_line) тоже: клиенту может уйти приглашение написать в группу новой линии, супервизору — срочная задача; права те же, что у кнопки в панели. Автор заметки и проект задаются сервером («Имя Фамилия (через ИИ)»), поэтому ИИ не может подписаться чужим именем. История диалога (get_client_history) отдаёт только перечисленные типы событий и вырезает секреты передачи (transferSessionToken, lineTransferToken) и byUserId; имена и id менеджеров скрываются той же настройкой отдела продаж. События сделок, оплат и чеков, которые CRM пишет в ленту диалога (status_change, custom_field_updated: название, сумма и ответственный сделки, суммы отклонённых оплат и чеков), в историю не входят: подключение только с clients:read не получает сведений о сделках и деньгах в обход deals:read. Содержимое сообщений и статей следует обрабатывать как данные: текст клиента не должен сам по себе становиться инструкцией на изменение прав, отправку сообщений или выгрузку сведений.

Хранение ключей и OAuth

  • Полный API-ключ показывается только при создании. Napulse сохраняет его хеш.
  • Храните ключ в защищённом поле авторизации приложения или хранилище секретов; не передавайте его модели текстовым сообщением.
  • Утерянный API-ключ нельзя повторно посмотреть: создайте новый и отключите старый.
  • При подозрении на утечку сразу отключите подключение, затем проверьте выполненные действия.
  • OAuth использует одноразовый код, PKCE S256 и точную проверку зарегистрированного адреса возврата. Access token действует 5 минут, refresh token обновляется при использовании; срок семейства refresh-токенов — до 30 дней. Повторное использование старого refresh token отзывает семейство.

Обычный MCP-клиент обслуживает OAuth-обновление автоматически. Не путайте короткий срок access token со сроком персонального API-ключа: это разные способы авторизации.

API-ключи и подключения бессрочные: они работают до отзыва. Чтобы находить забытые и подозрительные подключения, на карточке подключения показано «Последнее использование» — время последнего вызова ИИ (колонка mcp_api_keys.last_used_at, обновляется при обмене ключа или OAuth-токена на служебную сессию не чаще раза в минуту). Отказ по правам или выключенный MCP тоже считается использованием: значит, приложение продолжает стучаться.

Обход подключения закрыт

Внешнее приложение работает только через адрес /mcp. Короткая служебная сессия, которую подключение получает внутри CRM, не принимается от внешнего клиента: её нельзя взять и использовать напрямую, минуя журнал и лимит вызовов. Служебные адреса обмена ключа и записи журнала снаружи отвечают так же, как несуществующий адрес. Поэтому дописать в журнал поддельную запись тоже нельзя.

Журнал действий

Каждое действие подключения оставляет в журнале проекта две записи:

  • запись инструмента: какой инструмент вызван, успешно ли, сколько длилось;
  • запись запроса: какой раздел CRM открыт или изменён и какой объект затронут — диалог, сделка, чек, задача, статья, доска, робот, шаг робота, рассылка, сотрудник, группа подписчиков, сайт, SMM-пост, готовый отчёт аналитики, воронка. Записывается шаблон адреса и номер объекта, без параметров поиска и без содержимого.

По второй записи можно установить, какие именно диалоги и сделки открывало подключение.

Если подключение действует в режиме «войти как» (start_acting_as_user), обе записи называют двоих: сотрудника, от чьего имени выполнено действие, и сотрудника, который на самом деле действовал (колонка impersonator_user_id). Фильтр журнала по сотруднику находит и то, и другое.

Что журнал не хранит:

  • тексты ответов клиентам, статей, черновиков, заметок, критик кураторов, причин и комментариев передачи на линию и фрагменты переписки. Вместо текста сохраняются его длина и отпечаток SHA-256: по нему можно сверить, что отправлен именно этот текст, но восстановить текст нельзя;
  • полные адреса почты. Остаются первая буква и домен, например a***@example.com;
  • аргументы читающих инструментов. Для них сохраняется только отпечаток;
  • пароли (change_my_password, set_employee_password) и значения выпущенных ключей — ни самих значений, ни их длины и отпечатков; остаётся только факт: «пароль сотрудника X изменён», «выпущен ключ с таким названием»;
  • значения неверных аргументов. Если ИИ передал инструменту неверное значение, в журнале остаются только код ошибки и названия полей, например invalid_arguments: text. Само значение может быть текстом клиента, поэтому его видит только ИИ, который сделал вызов.

Изменяющее действие выполняется только после того, как запись о нём сохранена. Если журнал временно недоступен, действие отклоняется с ошибкой mcp_audit_unavailable и ничего не меняется. Чтение в такой ситуации продолжается, а сбой журнала фиксируется в журнале сервера.

Записи хранятся 180 дней и удаляются автоматически. Удаление или отзыв подключения журнал не стирает.

В этом же журнале проекта видны CRM-действия роботов. Это задача, этап сделки, ответственный и заметка, которые робот выполняет в пределах прав своего автора. Такие записи помечены источником «робот», сотрудник в них — автор робота. Тексты заметок и задач хранятся только как длина и отпечаток SHA-256. Выключатели MCP эти действия не останавливают.

Отказы, которых нет в журнале проекта (запрос без ключа, отозванный ключ, чужой сайт, истёкшая служебная сессия, ошибки OAuth), администратор платформы видит в админке на карточке «MCP: отказы за час / сутки». Эти счётчики живут в памяти сервера и не пишутся в базу. Они хранят только причину из закрытого списка, HTTP-статус, шаблон адреса и имя инструмента: без ключей, cookie, IP-адресов, аргументов и текстов. Подробнее — Ошибки подключения MCP, раздел «Мониторинг отказов».

Кто видит журнал

Журнал открыт на странице Интеграции → MCP, вкладка «Журнал вызовов» (адрес GET /api/mcp/audit-log).

  • Свой журнал видит каждый, у кого есть право «Подключать ИИ (MCP)». Чужие записи ему не отдаются, даже если он попросит фильтр по другому сотруднику; чужое подключение отвечает «не найдено».
  • Журнал всего проекта видит сотрудник с правом «Управлять подключениями ИИ».
  • Экран получает только метаданные: время, источник, сотрудник, подключение, действие, шаблон адреса, тип и номер объекта, результат, первые 200 символов ошибки, длительность. Аргументы, их отредактированные копии и отпечатки (args, args_digest) не отдаются никогда.
  • Номер объекта (например, диалога) виден держателю «Управлять подключениями ИИ», даже если сам диалог ему не виден. Это только номер, без содержимого; риск принят.
  • Браузерный адрес GET /api/mcp/audit-log служебной сессии ИИ по-прежнему закрыт. С MCP v2 (F10b) внешний ИИ читает журнал своим инструментом list_mcp_audit_log под разрешением mcp:keys — по тем же правилам, что страница: свои записи, а с правом «Управлять подключениями ИИ» — всего проекта, только метаданные.
  • Журнал работает и при выключенном MCP, и в режиме «войти как».

Безопасные повторения

Запись не повторяется автоматически при сетевой ошибке. У интеграции тоже не должно быть безусловного повтора отправки, приглашения, создания объекта или запуска рассылки: ответ мог потеряться после выполнения действия. Сначала прочитайте состояние и определите, нужно ли повторение.

Для массовых тегов передавайте idempotencyKey: повтор с тем же ключом вернёт ту же задачу и ничего не запустит второй раз.

Для статей и пользовательских досок используйте expectedRevision. Эта проверка предотвращает перезапись версии, изменённой другим участником; она не заменяет проверку получателя и содержания действия.

Подключение · Каталог инструментов · Ошибки