@retensy/mcp 0.20.0 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +13 -13
- package/.claude-plugin/plugin.json +12 -12
- package/README.md +334 -322
- package/package.json +2 -2
- package/skills/build-bot-funnel/SKILL.md +108 -108
- package/skills/build-bot-funnel/reference/schema.md +483 -479
- package/skills/build-bot-funnel/reference/validation.md +134 -134
- package/skills/build-bot-funnel/validate.mjs +680 -649
- package/skills/build-site/SKILL.md +105 -105
- package/skills/send-broadcast/SKILL.md +49 -49
- package/src/index.mjs +7 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retensy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"description": "MCP server to build and publish Telegram and MAX bot funnels/automations, broadcasts, service integrations, block-based websites (incl. free-layout Zero blocks, designs, templates, custom domains, form leads) and Markdown blog articles in the Retensy Bots service. Zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "node src/index.mjs",
|
|
23
23
|
"check": "node --check src/index.mjs",
|
|
24
|
-
"test": "node scripts/smoke.mjs && node scripts/telemetry.test.mjs && node scripts/live-edit-422.test.mjs && node scripts/broadcast-connect.test.mjs && node scripts/integration-core.test.mjs && node scripts/site-init.test.mjs && node scripts/ai-agents.test.mjs && node scripts/battery-tools.test.mjs && node scripts/parity-tools.test.mjs && node scripts/parity-2.test.mjs && node scripts/validate-graph.test.mjs",
|
|
24
|
+
"test": "node scripts/smoke.mjs && node scripts/telemetry.test.mjs && node scripts/live-edit-422.test.mjs && node scripts/broadcast-connect.test.mjs && node scripts/integration-core.test.mjs && node scripts/site-init.test.mjs && node scripts/ai-agents.test.mjs && node scripts/battery-tools.test.mjs && node scripts/parity-tools.test.mjs && node scripts/parity-2.test.mjs && node scripts/validate-graph.test.mjs && node scripts/call-scenario.test.mjs",
|
|
25
25
|
"validate": "node skills/build-bot-funnel/validate.mjs"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: build-bot-funnel
|
|
3
|
-
description: Собрать воронку (сценарий) бота для сервиса /bots из текстового описания — сгенерировать валидный граф формата retensy-bot-graph, проверить его и (через MCP bot-graph) залить в бота, прогнать dry-run и опубликовать. Поддерживает Telegram и MAX (Instagram сейчас выключен в сервисе — IG-бота не подключить). Использовать, когда пользователь описывает воронку/прогрев/сценарий бота словами и просит «собрать», «сделать граф», «залить в бота», «опубликовать сценарий».
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Сборка воронки бота (retensy /bots)
|
|
7
|
-
|
|
8
|
-
Превращает **текстовое описание воронки** в валидный граф `retensy-bot-graph` и (опционально) публикует его через MCP-сервер `bot-graph`. Поддерживаются платформы **Telegram и MAX** — у каждой свои триггеры и ограничения, описанные в schema.md. **Instagram сейчас выключен в сервисе — IG-бота не подключить**; разделы о нём оставлены на будущее включение.
|
|
9
|
-
|
|
10
|
-
**Различия платформ (кратко):**
|
|
11
|
-
- **Telegram** — полный функционал: `/start` и другие команды, все типы узлов, SUBSCRIBED, кнопки-контакты, рассылки.
|
|
12
|
-
- **MAX** — те же узлы, но без SUBSCRIBED/reply-клавиатур (мягкие предупреждения при публикации, не блокируют).
|
|
13
|
-
- **Instagram** (сейчас выключен в сервисе — IG-бота не подключить) — ограниченный набор узлов; вход только через комментарий/директ/историю (нет `/start`); нет рассылок; DELAY не более 24ч; ASK_QUESTION только TEXT/EMAIL/PHONE/NUMBER/CONTACT (CONTACT = ручной ввод номера, кнопки «Поделиться номером» в IG нет); онбординг через OAuth на странице `/bots/instagram` («Подключения» → Instagram), без токена бота.
|
|
14
|
-
|
|
15
|
-
## Когда применять
|
|
16
|
-
Пользователь описывает сценарий бота словами: приветствие → подписка → вопрос с кнопками → ветки → задержки → вебинар и т.п. Либо просит залить/опубликовать готовую воронку.
|
|
17
|
-
|
|
18
|
-
## Рабочий процесс (делай по шагам)
|
|
19
|
-
|
|
20
|
-
1. **Уточни вход.** Если описание неполное, спроси кратко: точка(и) входа (команда `/start` и т.п.), тексты сообщений, кнопки и куда они ведут, развилки/условия, задержки, финал. Не выдумывай маркетинговые тексты — проси у пользователя или помечай `[ВСТАВЬ ТЕКСТ]` только если он сам разрешил.
|
|
21
|
-
|
|
22
|
-
2. **Спланируй граф.** Нарисуй в голове дерево: триггеры → сообщения → ветки. Каждой кнопке-выбору — свой выходной хэндл (`btn_0`, `btn_1`…). Подробности типов узлов и хэндлов: [reference/schema.md](reference/schema.md).
|
|
23
|
-
|
|
24
|
-
3. **Сгенерируй JSON** формата `retensy-bot-graph` (см. схему). Требования к корректности (иначе не опубликуется) — в [reference/validation.md](reference/validation.md). Главное:
|
|
25
|
-
- У каждого `SEND_MESSAGE` непустой `config.text` (и продублируй в `cards[0].text`).
|
|
26
|
-
- id узлов и рёбер — валидные UUID, уникальные.
|
|
27
|
-
- Есть хотя бы один корневой `TRIGGER_*`; все нелистовые узлы достижимы от триггера; синхронных циклов нет (цикл только через `ASK_QUESTION`/`DELAY`/`SCHEDULE`).
|
|
28
|
-
- Кнопки-выборы разведены по хэндлам `btn_N` — у такой `CALLBACK`-кнопки `value` ОБЯЗАТЕЛЬНО пустой (`""`): бот сам сгенерит `callback_data`, непустой `value` ломает переход (`NO_MATCH`). Цвет кнопки — только `""`/`#34C759`/`#FF3B30`. Условия `CONDITION` — `yes`/`no`; вопрос (`ASK_QUESTION` или `SEND_MESSAGE` с `awaitReply:true`) — `valid`/`invalid`.
|
|
29
|
-
- **Не добавляй узлы `END`** — ветка завершается сама на узле без исходящих рёбер; явный «конец сценария» убран из редактора.
|
|
30
|
-
- `SEND_MESSAGE` умеет быть и сообщением, и **вопросом** (`awaitReply:true` + `saveTo`/`inputKind`/`validator`). У кнопок-ссылок (`kind:"URL"`) есть флаг `track:true` — на такой шаг ссылается условие `LINK_CLICKED`.
|
|
31
|
-
- **Медиа** (фото/видео/аудио/документ/кружок/галерея): загрузи файл инструментом `upload_file` (локальный `path` или `url` для перезаливки) → получишь публичный URL → вставь его в медиа-карточку `SEND_MESSAGE` (`url`, у `gallery` — `urls[]`) или в `SEND_PHOTO.photoUrl`. Уже загруженное — `list_files`. Бинарь в графе не хранится, только ссылки. Подробности — в schema.md.
|
|
32
|
-
- `CONDITION` умеет: теги, переменные, UTM-метки, имя/email/телефон из профиля, @username, подписку на канал (`SUBSCRIBED`), клик по ссылке шага (`LINK_CLICKED`), дату/время/день недели. Полная таблица `kind`/`op` — в schema.md.
|
|
33
|
-
- Пакет `ACTIONS` — до 30 действий (метки, профиль, HTTP, уведомления, интеграции GetCourse/amoCRM/Битрикс24/Google Sheets/Я.Метрика, приём оплат, модерация группы). Список — в schema.md. **Интеграции РАБОТАЮТ** (это не заглушки), но каждой нужен `connectionId` — возьми его через **`list_integrations`**; без него действие упадёт «не выбрано подключение». Для Google Таблиц вместо `connectionId` нужен `googleEmail` (аккаунт подключается в вебе).
|
|
34
|
-
- `AI_REPLY` («AI-агент») — два режима, поле `mode` (`"simple"`/не задан, либо `"agent"`). Платный узел в обоих режимах (`PREMIUM_NODE_FORBIDDEN` на бесплатном тарифе) и недоступен в Instagram-графах (нет в IG-allowlist). Тратит месячный AI-бюджет тарифа — он в токенах, не в рублях.
|
|
35
|
-
- **`mode` не задан или `"simple"`** — ответ модели по `systemPrompt`/`userPromptTemplate` (обязателен, иначе выход `error`). Исчерпание бюджета — не ошибка: узел шлёт `quotaFallbackText` (если задан) и идёт дальше по `next`, как обычно.
|
|
36
|
-
- **`mode: "agent"`** — поиск по подключённой базе знаний (`knowledgeBaseId`), ветки-действия (`actions[]`, каждому — своё ребро `action_<id>`), извлечение переменных из разговора (`extract[]`, значения — слова клиента, не проверенный факт) и готовая сводка на действии в `{{var.ai_summary}}` без отдельной настройки. Без найденных в базе фрагментов (нет базы, либо релевантность ниже порога) агент в ЛЮБОМ режиме (строгом и нет) уходит в `unknown` — модель даже не вызывается; исключение — узел с непустым `actions[]`: он и без фрагментов зовёт модель, чтобы та могла выбрать действие («запишите меня на завтра» слов из базы не содержит), а её ответ без фрагментов в любом режиме (и в нестрогом тоже) уходит в `unknown`; нестрогий режим лишь разрешает модели дополнять УЖЕ найденные фрагменты общими знаниями (кроме цен/сроков/обещаний), а не отвечать вовсе без базы. **У узла нет выхода `next`** — только `answered`/`action_<id>`/`unknown`/`budget_exhausted`/`error` (`error` — сбой модели/поиска, ИИ не настроен, headless-вебхук или исчерпанный бюджет без ветки `budget_exhausted`). Исчерпание бюджета — своя ветка `budget_exhausted` (если подключена) либо `error`, `quotaFallbackText` отправляется в обоих случаях. Если собираешь агента этим скиллом — обязательно веди себя по этому списку выходов, старый одиночный `AI_REPLY` без `mode` для агента не годится. `systemPrompt`: мягкая норма 10 000 символов (у бэкенда для неё нет отдельного предупреждения, только `validate.mjs` этого скилла warn'ит явно), жёсткий потолок 30 000 (`AGENT_PROMPT_TOO_LONG`, блокирует публикацию) — платформа сама добавляет защиту от инъекций/вытягивания инструкции, запрет обещать несделанное и формат под мессенджер, дублировать это в `systemPrompt` не нужно. Подробности всех полей и кодов ошибок (`AGENT_*`) — в schema.md/validation.md.
|
|
37
|
-
- **Приём оплат** — действие `issue_invoice` внутри `ACTIONS` (НЕ отдельный узел: `YOOKASSA_PAYMENT` устарел). Деньги идут на кассу владельца бота; `connectionId` — подключение `provider=YOOKASSA` из `list_integrations`. Два режима, выбирай по задаче:
|
|
38
|
-
- **дождаться оплаты прямо в диалоге** — протяни от блока ветку `paid` (появятся выходы `paid`/`timeout`); сценарий встанет на паузу до подтверждения кассой;
|
|
39
|
-
- **не ждать** — ветку `paid` не рисуй, блок просто пришлёт счёт и пойдёт по `next`; выдать доступ потом сможет отдельный сценарий с триггером `TRIGGER_PAYMENT`.
|
|
40
|
-
Счёт обязан быть **последним** действием в блоке и только одним (`INVOICE_NOT_LAST`, `INVOICE_DUPLICATE`).
|
|
41
|
-
- **`TRIGGER_PAYMENT` («оплата прошла»)** — если счёт выставлял бот, подписчик определяется САМ (счёт помнит его в metadata платежа): можно сразу слать сообщение и вешать метку, разбирать ответ кассы не нужно. Если оплата пришла мимо бота — прогон headless, получателя задавай через `config.target`. Ставь фильтр `minAmount`/`descriptionContains`, иначе сценарий сработает на любую оплату в кассе.
|
|
42
|
-
- **`TRIGGER_SCHEDULE` («По расписанию»)** — `{cron:"0 9 * * 1-5", timezone}`: headless-прогон без чата (отправка — только с `config.target`), для сводок, эскалаций, SLA. Журнал — `scenario_runs`.
|
|
43
|
-
- **Запись на слоты и напоминание**: календарь создаётся `booking_calendar_create`; в `ACTIONS` — `booking_slots` (список в `{{var.slots}}`) → вопрос → `booking_book` (ветка `taken` — слот занят) → `DELAY UNTIL {{var.slot_at}}` с `offset:"-24h"` → напоминание; `booking_cancel` освобождает слот. **Подписки**: `invite_link_create`/`invite_link_revoke`, `subscription_extend`/`subscription_check`, `yookassa_charge_saved`. **Передача оператору** — `agent_chat` (бот замолкает до «Вернуть боту»). Поля — в schema.md.
|
|
44
|
-
- **Статус прогона `PARTIAL`** («завершён с ошибками») — сценарий дошёл до конца, но действие упало (шаг `ok:false`, `error` «КОД: …»). Проверяй `bot_user_runs`/`scenario_runs`, а ответы CRM/HTTP — `integration_calls`.
|
|
45
|
-
- **`TRIGGER_SITE_FORM` («Заявка с сайта»)** — `{siteId, formId}`: посетитель отправил форму на ОПУБЛИКОВАННОМ сайте владельца (`site_list` → id сайта, id формы — из `site_get`). Пустой/отсутствующий `formId` = любая форма сайта. Поля формы — `{{var.<имя поля>}}`; имя, почта, телефон всегда доступны как `{{var.name}}`, `{{var.email}}`, `{{var.phone}}` (если поле названо иначе — находятся по подписи), UTM — `{{var.utm_source}}` и т.п. Чата с посетителем нет: узлы отправки работают только с явным `config.target`, CRM/таблицы/вебхуки — как обычно. Обычно это сценарий без бота (источник WEBHOOK, как вебхук-сценарий) — ему `TRIGGER_WEBHOOK` не нужен; можно и в сценарии бота. Чужой сайт → `SITE_FORM_FOREIGN_SITE`, без сайта → `SITE_FORM_NO_SITE`. Одна заявка — один запуск; `TRIGGER_ANY_UPDATE` на заявки не срабатывает.
|
|
46
|
-
|
|
47
|
-
4. **Проверь локально** перед заливкой:
|
|
48
|
-
```bash
|
|
49
|
-
node "<путь к скиллу>/validate.mjs" <путь к import.json>
|
|
50
|
-
# Для IG-бота:
|
|
51
|
-
node "<путь к скиллу>/validate.mjs" <путь к import.json> --platform=INSTAGRAM
|
|
52
|
-
# Для MAX-бота:
|
|
53
|
-
node "<путь к скиллу>/validate.mjs" <путь к import.json> --platform=MAX
|
|
54
|
-
```
|
|
55
|
-
Скрипт ловит пустые сообщения, висячие рёбра, дубли и не-UUID id узлов/рёбер, недостижимые узлы, превышение 4096, кривые DELAY (вкл. `duration`+`unit`), непустой `value` у кнопок-выборов с ребром `btn_N`, неподдерживаемые цвета. При `--platform=INSTAGRAM` дополнительно: IG-allowlist узлов (`IG_NODE_UNSUPPORTED`), DELAY > 24ч (`IG_DELAY_OVER_24H`), неподдерживаемый inputKind (`IG_INPUT_UNSUPPORTED`). Исправь всё, что он покажет.
|
|
56
|
-
|
|
57
|
-
5. **Залей и опубликуй через MCP `bot-graph`** (если он подключён и пользователь просит публикацию):
|
|
58
|
-
- `list_bots` → выбрать `botId` (или `create_graph` в существующем боте).
|
|
59
|
-
- `create_graph(botId, name)` → получить `graphId`. Либо стартуй с готовой основы: `list_templates` → `create_graph_from_template(botId, templateId, name)`.
|
|
60
|
-
- `update_graph(graphId, nodes, edges, canvasMeta)` → залить узлы/рёбра.
|
|
61
|
-
- `dry_run(graphId, kind:"command", value:"start")` → прогнать стартовую ветку, проверить `runStatus`. `dry_run` ничего не делает снаружи: внешние запросы, CRM, таблицы, письма, уведомления, счета и ответы ИИ пропускаются (у шага `skipped: "dry-run"`), переменные из их ответов остаются пустыми — это проверяется только живым прогоном.
|
|
62
|
-
- `publish_graph(graphId)` → при отказе инструмент вернёт ошибку `HTTP 422` со ВСЕМИ причинами построчно (`code@nodeId: message`) — разобрать, починить узлы, обновить, опубликовать снова.
|
|
63
|
-
- Управление сценариями: `clone_graph`, `rename_graph`, `set_active_graph` (переключить живой граф), `delete_graph` (активный нельзя — сначала переключи).
|
|
64
|
-
Если MCP не подключён — отдай готовый `import.json` и подскажи: /bots → граф → **Импорт**.
|
|
65
|
-
|
|
66
|
-
5b. **Правка СУЩЕСТВУЮЩЕГО / живого сценария — по умолчанию `edit_graph_live`, а НЕ clone+publish.**
|
|
67
|
-
- Когда пользователь просит «поправь сценарий X» (особенно если он уже открыт в редакторе или опубликован) — правь **ТОТ ЖЕ `graphId`** через **`edit_graph_live(graphId, nodes, edges)`**. Он сам снимает авто-бэкап предыдущего состояния (один rolling-граф «🔙 Авто-бэкап») и делает PUT на месте — **id не меняется**. Живой сценарий бота (бот-сценария) — граф с `isActive: true` в `list_graphs` (статус `PUBLISHED`; после `publish_graph` черновика это `publishedGraphId`, а **не** id черновика) — правь его id, иначе правка ляжет в черновик и до бота не дойдёт. Вебхук-сценарий отдельного опубликованного графа не имеет — правь его по его же id.
|
|
68
|
-
- Почему так: бэкенд при PUT/публикации шлёт `external_update` в WS-комнату → открытые редакторы перечитывают граф **вживую** (юзеру не надо перезаходить). Бот читает активный граф **заново из БД на каждое сообщение** → правка живого PUBLISHED-графа применяется **сразу, без отдельной публикации**.
|
|
69
|
-
- `clone_graph`+`publish_graph` НЕ создают новый живой граф и НЕ песочница: публикация копирует клон (как любой черновик) в id ТЕКУЩЕГО опубликованного графа бота и затирает его содержимое без бэкапа — вместе с живыми правками, сделанными после прошлой публикации; `set_active_graph` на прежний id после этого вернёт уже новое содержимое; открытые редакторы живого графа перечитают его (у кого есть несохранённые правки — получат просьбу обновить страницу). Клон годится, чтобы спокойно собрать крупный рискованный рефактор в черновике, но перед его публикацией сохрани живой граф: `get_graph(graphId, saveToFile)` — откат = залить файл обратно через `edit_graph_live`/`update_graph` с `graphFile`.
|
|
70
|
-
- ⚠️ PUT **активного** (PUBLISHED) графа сервер проверяет как `publish`: валидатор, платные блоки, лимит блоков тарифа, платформа. Ошибка → `HTTP 422` со всеми `code@nodeId`, граф **не сохранён**, бот работает на прежней версии. Черновик (`DRAFT`) PUT сохраняет без проверок (кроме размера: больше 4 МБ → 422 `GRAPH_TOO_LARGE`), и на бота он не влияет: `publish_graph` черновика копирует его в отдельный `PUBLISHED`-граф (`publishedGraphId`), а сам черновик остаётся `DRAFT`. Всё равно прогоняй `validate.mjs` + `dry_run` заранее. Откат: версии активного не хранятся — опубликовать граф «🔙 Авто-бэкап» (или скопировать его содержимое обратно).
|
|
71
|
-
|
|
72
|
-
6. **Отчитайся**: сколько узлов/веток, какие тексты помечены на проверку, ссылка/ id графа.
|
|
73
|
-
|
|
74
|
-
## Важные ограничения
|
|
75
|
-
- **Источник = текст** (этот режим). Если просят распознать с приватной Miro-доски — самый надёжный путь: CSV-экспорт из Miro; либо запуск залогиненного Chrome пользователя и съёмка экрана (headless WebGL-холст Miro не отдаёт). Это отдельный сценарий, не основной для этого скилла.
|
|
76
|
-
- **Авторизация MCP** — персональный токен (создаётся в вебе на `/bots/mcp-tokens`, формат `zmcp_…`, полный доступ). Если инструмент вернул «нет токена» или ошибку доступа — **вызови `setup`**, объясни пользователю шаги, попроси прислать токен и сохрани его через **`set_token`** (применяется сразу, без env/рестарта). Также работают env `RETENSY_MCP_TOKEN` и session-cookie.
|
|
77
|
-
- Бэкенд читает плоские поля `config.text`/`config.photoUrl`; редактор берёт текст из первой карточки `type:"text"`. Поэтому **всегда заполняй и `text`, и `cards`**.
|
|
78
|
-
- **Размер графа ≤ 4 МБ** (JSON `{nodes, edges, canvasMeta}` в UTF-8): больше — `HTTP 422 GRAPH_TOO_LARGE` на `update_graph`/`edit_graph_live`/`patch_graph`/`import_funnel`, и у черновика тоже; граф не сохранён. `validate.mjs` проверяет заранее.
|
|
79
|
-
- **Переменные подписчика:** строка ≤ 65 536 символов (длиннее обрезается), список/объект длиннее не сохраняется; все переменные одного подписчика вместе с именами ≤ 262 144 символов — запись сверх лимита не сохраняется. Большой ответ API не клади в переменную целиком — вытаскивай нужное через `extract`.
|
|
80
|
-
|
|
81
|
-
## Если возможности не хватает — она фиксируется автоматически
|
|
82
|
-
|
|
83
|
-
При каждой неудаче инструмента отправляется **анонимный** отчёт на `POST /api/mcp/report` того же
|
|
84
|
-
сервера retensy (неизвестный инструмент, отказ публикации `errors[]`, ошибка API). Так мы узнаём,
|
|
85
|
-
чего людям не хватает, и добавляем это в продукт.
|
|
86
|
-
|
|
87
|
-
Что это значит для тебя как ассистента:
|
|
88
|
-
- **Не выдумывай обходные пути для того, чего нет в schema.md.** Просят узел/триггер/действие,
|
|
89
|
-
которого в схеме нет, — скажи прямо, что этого пока нет, и что запрос уже зарегистрирован
|
|
90
|
-
автоматически. Не подменяй это самодельным костылём молча.
|
|
91
|
-
- **Не повторяй один и тот же падающий вызов по кругу.** Отчёты дедуплицируются, но бессмысленные
|
|
92
|
-
ретраи всё равно тратят время пользователя. Разбери `code`/`nodeId` и исправь причину.
|
|
93
|
-
- Секреты в отчёт не попадают: токены, cookie и креды интеграций вырезаются, значения аргументов
|
|
94
|
-
по умолчанию скрыты. Пользователь может выключить отчёты через `RETENSY_MCP_TELEMETRY=off`.
|
|
95
|
-
- **Нужна помощь человека** — если что-то не работает или вопрос не про сборку сценария, направляй
|
|
96
|
-
пользователя в чат «Поддержка» в кабинете bots.retensy.com (кнопка внизу бокового меню): там
|
|
97
|
-
отвечает менеджер. Внешние контакты поддержки не называй.
|
|
98
|
-
|
|
99
|
-
## Версия сервера
|
|
100
|
-
|
|
101
|
-
При старте сервер сверяется с npm. Если вышла новая версия — уведомление появится в `setup` и
|
|
102
|
-
в первом ответе инструмента. **Увидел такое уведомление — передай его пользователю**: обновление
|
|
103
|
-
вступает в силу только ПОСЛЕ перезапуска MCP-сервера, сам процесс свой код подменить не может.
|
|
104
|
-
|
|
105
|
-
## Файлы скилла
|
|
106
|
-
- [reference/schema.md](reference/schema.md) — формат графа, типы узлов, конфиги, хэндлы.
|
|
107
|
-
- [reference/validation.md](reference/validation.md) — правила валидатора бэкенда (коды ошибок) и `cardsToLegacy`.
|
|
108
|
-
- `validate.mjs` — оффлайн-проверка графа перед заливкой.
|
|
1
|
+
---
|
|
2
|
+
name: build-bot-funnel
|
|
3
|
+
description: Собрать воронку (сценарий) бота для сервиса /bots из текстового описания — сгенерировать валидный граф формата retensy-bot-graph, проверить его и (через MCP bot-graph) залить в бота, прогнать dry-run и опубликовать. Поддерживает Telegram и MAX (Instagram сейчас выключен в сервисе — IG-бота не подключить). Использовать, когда пользователь описывает воронку/прогрев/сценарий бота словами и просит «собрать», «сделать граф», «залить в бота», «опубликовать сценарий».
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Сборка воронки бота (retensy /bots)
|
|
7
|
+
|
|
8
|
+
Превращает **текстовое описание воронки** в валидный граф `retensy-bot-graph` и (опционально) публикует его через MCP-сервер `bot-graph`. Поддерживаются платформы **Telegram и MAX** — у каждой свои триггеры и ограничения, описанные в schema.md. **Instagram сейчас выключен в сервисе — IG-бота не подключить**; разделы о нём оставлены на будущее включение.
|
|
9
|
+
|
|
10
|
+
**Различия платформ (кратко):**
|
|
11
|
+
- **Telegram** — полный функционал: `/start` и другие команды, все типы узлов, SUBSCRIBED, кнопки-контакты, рассылки.
|
|
12
|
+
- **MAX** — те же узлы, но без SUBSCRIBED/reply-клавиатур (мягкие предупреждения при публикации, не блокируют).
|
|
13
|
+
- **Instagram** (сейчас выключен в сервисе — IG-бота не подключить) — ограниченный набор узлов; вход только через комментарий/директ/историю (нет `/start`); нет рассылок; DELAY не более 24ч; ASK_QUESTION только TEXT/EMAIL/PHONE/NUMBER/CONTACT (CONTACT = ручной ввод номера, кнопки «Поделиться номером» в IG нет); онбординг через OAuth на странице `/bots/instagram` («Подключения» → Instagram), без токена бота.
|
|
14
|
+
|
|
15
|
+
## Когда применять
|
|
16
|
+
Пользователь описывает сценарий бота словами: приветствие → подписка → вопрос с кнопками → ветки → задержки → вебинар и т.п. Либо просит залить/опубликовать готовую воронку.
|
|
17
|
+
|
|
18
|
+
## Рабочий процесс (делай по шагам)
|
|
19
|
+
|
|
20
|
+
1. **Уточни вход.** Если описание неполное, спроси кратко: точка(и) входа (команда `/start` и т.п.), тексты сообщений, кнопки и куда они ведут, развилки/условия, задержки, финал. Не выдумывай маркетинговые тексты — проси у пользователя или помечай `[ВСТАВЬ ТЕКСТ]` только если он сам разрешил.
|
|
21
|
+
|
|
22
|
+
2. **Спланируй граф.** Нарисуй в голове дерево: триггеры → сообщения → ветки. Каждой кнопке-выбору — свой выходной хэндл (`btn_0`, `btn_1`…). Подробности типов узлов и хэндлов: [reference/schema.md](reference/schema.md).
|
|
23
|
+
|
|
24
|
+
3. **Сгенерируй JSON** формата `retensy-bot-graph` (см. схему). Требования к корректности (иначе не опубликуется) — в [reference/validation.md](reference/validation.md). Главное:
|
|
25
|
+
- У каждого `SEND_MESSAGE` непустой `config.text` (и продублируй в `cards[0].text`).
|
|
26
|
+
- id узлов и рёбер — валидные UUID, уникальные.
|
|
27
|
+
- Есть хотя бы один корневой `TRIGGER_*`; все нелистовые узлы достижимы от триггера; синхронных циклов нет (цикл только через `ASK_QUESTION`/`DELAY`/`SCHEDULE`).
|
|
28
|
+
- Кнопки-выборы разведены по хэндлам `btn_N` — у такой `CALLBACK`-кнопки `value` ОБЯЗАТЕЛЬНО пустой (`""`): бот сам сгенерит `callback_data`, непустой `value` ломает переход (`NO_MATCH`). Цвет кнопки — только `""`/`#34C759`/`#FF3B30`. Условия `CONDITION` — `yes`/`no`; вопрос (`ASK_QUESTION` или `SEND_MESSAGE` с `awaitReply:true`) — `valid`/`invalid`.
|
|
29
|
+
- **Не добавляй узлы `END`** — ветка завершается сама на узле без исходящих рёбер; явный «конец сценария» убран из редактора.
|
|
30
|
+
- `SEND_MESSAGE` умеет быть и сообщением, и **вопросом** (`awaitReply:true` + `saveTo`/`inputKind`/`validator`). У кнопок-ссылок (`kind:"URL"`) есть флаг `track:true` — на такой шаг ссылается условие `LINK_CLICKED`.
|
|
31
|
+
- **Медиа** (фото/видео/аудио/документ/кружок/галерея): загрузи файл инструментом `upload_file` (локальный `path` или `url` для перезаливки) → получишь публичный URL → вставь его в медиа-карточку `SEND_MESSAGE` (`url`, у `gallery` — `urls[]`) или в `SEND_PHOTO.photoUrl`. Уже загруженное — `list_files`. Бинарь в графе не хранится, только ссылки. Подробности — в schema.md.
|
|
32
|
+
- `CONDITION` умеет: теги, переменные, UTM-метки, имя/email/телефон из профиля, @username, подписку на канал (`SUBSCRIBED`), клик по ссылке шага (`LINK_CLICKED`), дату/время/день недели. Полная таблица `kind`/`op` — в schema.md.
|
|
33
|
+
- Пакет `ACTIONS` — до 30 действий (метки, профиль, HTTP, уведомления, интеграции GetCourse/amoCRM/Битрикс24/Google Sheets/Я.Метрика, приём оплат, модерация группы). Список — в schema.md. **Интеграции РАБОТАЮТ** (это не заглушки), но каждой нужен `connectionId` — возьми его через **`list_integrations`**; без него действие упадёт «не выбрано подключение». Для Google Таблиц вместо `connectionId` нужен `googleEmail` (аккаунт подключается в вебе).
|
|
34
|
+
- `AI_REPLY` («AI-агент») — два режима, поле `mode` (`"simple"`/не задан, либо `"agent"`). Платный узел в обоих режимах (`PREMIUM_NODE_FORBIDDEN` на бесплатном тарифе) и недоступен в Instagram-графах (нет в IG-allowlist). Тратит месячный AI-бюджет тарифа — он в токенах, не в рублях.
|
|
35
|
+
- **`mode` не задан или `"simple"`** — ответ модели по `systemPrompt`/`userPromptTemplate` (обязателен, иначе выход `error`). Исчерпание бюджета — не ошибка: узел шлёт `quotaFallbackText` (если задан) и идёт дальше по `next`, как обычно.
|
|
36
|
+
- **`mode: "agent"`** — поиск по подключённой базе знаний (`knowledgeBaseId`), ветки-действия (`actions[]`, каждому — своё ребро `action_<id>`), извлечение переменных из разговора (`extract[]`, значения — слова клиента, не проверенный факт) и готовая сводка на действии в `{{var.ai_summary}}` без отдельной настройки. Без найденных в базе фрагментов (нет базы, либо релевантность ниже порога) агент в ЛЮБОМ режиме (строгом и нет) уходит в `unknown` — модель даже не вызывается; исключение — узел с непустым `actions[]`: он и без фрагментов зовёт модель, чтобы та могла выбрать действие («запишите меня на завтра» слов из базы не содержит), а её ответ без фрагментов в любом режиме (и в нестрогом тоже) уходит в `unknown`; нестрогий режим лишь разрешает модели дополнять УЖЕ найденные фрагменты общими знаниями (кроме цен/сроков/обещаний), а не отвечать вовсе без базы. **У узла нет выхода `next`** — только `answered`/`action_<id>`/`unknown`/`budget_exhausted`/`error` (`error` — сбой модели/поиска, ИИ не настроен, headless-вебхук или исчерпанный бюджет без ветки `budget_exhausted`). Исчерпание бюджета — своя ветка `budget_exhausted` (если подключена) либо `error`, `quotaFallbackText` отправляется в обоих случаях. Если собираешь агента этим скиллом — обязательно веди себя по этому списку выходов, старый одиночный `AI_REPLY` без `mode` для агента не годится. `systemPrompt`: мягкая норма 10 000 символов (у бэкенда для неё нет отдельного предупреждения, только `validate.mjs` этого скилла warn'ит явно), жёсткий потолок 30 000 (`AGENT_PROMPT_TOO_LONG`, блокирует публикацию) — платформа сама добавляет защиту от инъекций/вытягивания инструкции, запрет обещать несделанное и формат под мессенджер, дублировать это в `systemPrompt` не нужно. Подробности всех полей и кодов ошибок (`AGENT_*`) — в schema.md/validation.md.
|
|
37
|
+
- **Приём оплат** — действие `issue_invoice` внутри `ACTIONS` (НЕ отдельный узел: `YOOKASSA_PAYMENT` устарел). Деньги идут на кассу владельца бота; `connectionId` — подключение `provider=YOOKASSA` из `list_integrations`. Два режима, выбирай по задаче:
|
|
38
|
+
- **дождаться оплаты прямо в диалоге** — протяни от блока ветку `paid` (появятся выходы `paid`/`timeout`); сценарий встанет на паузу до подтверждения кассой;
|
|
39
|
+
- **не ждать** — ветку `paid` не рисуй, блок просто пришлёт счёт и пойдёт по `next`; выдать доступ потом сможет отдельный сценарий с триггером `TRIGGER_PAYMENT`.
|
|
40
|
+
Счёт обязан быть **последним** действием в блоке и только одним (`INVOICE_NOT_LAST`, `INVOICE_DUPLICATE`).
|
|
41
|
+
- **`TRIGGER_PAYMENT` («оплата прошла»)** — если счёт выставлял бот, подписчик определяется САМ (счёт помнит его в metadata платежа): можно сразу слать сообщение и вешать метку, разбирать ответ кассы не нужно. Если оплата пришла мимо бота — прогон headless, получателя задавай через `config.target`. Ставь фильтр `minAmount`/`descriptionContains`, иначе сценарий сработает на любую оплату в кассе.
|
|
42
|
+
- **`TRIGGER_SCHEDULE` («По расписанию»)** — `{cron:"0 9 * * 1-5", timezone}`: headless-прогон без чата (отправка — только с `config.target`), для сводок, эскалаций, SLA. Журнал — `scenario_runs`.
|
|
43
|
+
- **Запись на слоты и напоминание**: календарь создаётся `booking_calendar_create`; в `ACTIONS` — `booking_slots` (список в `{{var.slots}}`) → вопрос → `booking_book` (ветка `taken` — слот занят) → `DELAY UNTIL {{var.slot_at}}` с `offset:"-24h"` → напоминание; `booking_cancel` освобождает слот. **Подписки**: `invite_link_create`/`invite_link_revoke`, `subscription_extend`/`subscription_check`, `yookassa_charge_saved`. **Передача оператору** — `agent_chat` (бот замолкает до «Вернуть боту»). Поля — в schema.md.
|
|
44
|
+
- **Статус прогона `PARTIAL`** («завершён с ошибками») — сценарий дошёл до конца, но действие упало (шаг `ok:false`, `error` «КОД: …»). Проверяй `bot_user_runs`/`scenario_runs`, а ответы CRM/HTTP — `integration_calls`.
|
|
45
|
+
- **`TRIGGER_SITE_FORM` («Заявка с сайта»)** — `{siteId, formId}`: посетитель отправил форму на ОПУБЛИКОВАННОМ сайте владельца (`site_list` → id сайта, id формы — из `site_get`). Пустой/отсутствующий `formId` = любая форма сайта. Поля формы — `{{var.<имя поля>}}`; имя, почта, телефон всегда доступны как `{{var.name}}`, `{{var.email}}`, `{{var.phone}}` (если поле названо иначе — находятся по подписи), UTM — `{{var.utm_source}}` и т.п. Чата с посетителем нет: узлы отправки работают только с явным `config.target`, CRM/таблицы/вебхуки — как обычно. Обычно это сценарий без бота (источник WEBHOOK, как вебхук-сценарий) — ему `TRIGGER_WEBHOOK` не нужен; можно и в сценарии бота. Чужой сайт → `SITE_FORM_FOREIGN_SITE`, без сайта → `SITE_FORM_NO_SITE`. Одна заявка — один запуск; `TRIGGER_ANY_UPDATE` на заявки не срабатывает.
|
|
46
|
+
|
|
47
|
+
4. **Проверь локально** перед заливкой:
|
|
48
|
+
```bash
|
|
49
|
+
node "<путь к скиллу>/validate.mjs" <путь к import.json>
|
|
50
|
+
# Для IG-бота:
|
|
51
|
+
node "<путь к скиллу>/validate.mjs" <путь к import.json> --platform=INSTAGRAM
|
|
52
|
+
# Для MAX-бота:
|
|
53
|
+
node "<путь к скиллу>/validate.mjs" <путь к import.json> --platform=MAX
|
|
54
|
+
```
|
|
55
|
+
Скрипт ловит пустые сообщения, висячие рёбра, дубли и не-UUID id узлов/рёбер, недостижимые узлы, превышение 4096, кривые DELAY (вкл. `duration`+`unit`), непустой `value` у кнопок-выборов с ребром `btn_N`, неподдерживаемые цвета. При `--platform=INSTAGRAM` дополнительно: IG-allowlist узлов (`IG_NODE_UNSUPPORTED`), DELAY > 24ч (`IG_DELAY_OVER_24H`), неподдерживаемый inputKind (`IG_INPUT_UNSUPPORTED`). Исправь всё, что он покажет.
|
|
56
|
+
|
|
57
|
+
5. **Залей и опубликуй через MCP `bot-graph`** (если он подключён и пользователь просит публикацию):
|
|
58
|
+
- `list_bots` → выбрать `botId` (или `create_graph` в существующем боте).
|
|
59
|
+
- `create_graph(botId, name)` → получить `graphId`. Либо стартуй с готовой основы: `list_templates` → `create_graph_from_template(botId, templateId, name)`.
|
|
60
|
+
- `update_graph(graphId, nodes, edges, canvasMeta)` → залить узлы/рёбра.
|
|
61
|
+
- `dry_run(graphId, kind:"command", value:"start")` → прогнать стартовую ветку, проверить `runStatus`. `dry_run` ничего не делает снаружи: внешние запросы, CRM, таблицы, письма, уведомления, счета и ответы ИИ пропускаются (у шага `skipped: "dry-run"`), переменные из их ответов остаются пустыми — это проверяется только живым прогоном.
|
|
62
|
+
- `publish_graph(graphId)` → при отказе инструмент вернёт ошибку `HTTP 422` со ВСЕМИ причинами построчно (`code@nodeId: message`) — разобрать, починить узлы, обновить, опубликовать снова.
|
|
63
|
+
- Управление сценариями: `clone_graph`, `rename_graph`, `set_active_graph` (переключить живой граф), `delete_graph` (активный нельзя — сначала переключи).
|
|
64
|
+
Если MCP не подключён — отдай готовый `import.json` и подскажи: /bots → граф → **Импорт**.
|
|
65
|
+
|
|
66
|
+
5b. **Правка СУЩЕСТВУЮЩЕГО / живого сценария — по умолчанию `edit_graph_live`, а НЕ clone+publish.**
|
|
67
|
+
- Когда пользователь просит «поправь сценарий X» (особенно если он уже открыт в редакторе или опубликован) — правь **ТОТ ЖЕ `graphId`** через **`edit_graph_live(graphId, nodes, edges)`**. Он сам снимает авто-бэкап предыдущего состояния (один rolling-граф «🔙 Авто-бэкап») и делает PUT на месте — **id не меняется**. Живой сценарий бота (бот-сценария) — граф с `isActive: true` в `list_graphs` (статус `PUBLISHED`; после `publish_graph` черновика это `publishedGraphId`, а **не** id черновика) — правь его id, иначе правка ляжет в черновик и до бота не дойдёт. Вебхук-сценарий отдельного опубликованного графа не имеет — правь его по его же id.
|
|
68
|
+
- Почему так: бэкенд при PUT/публикации шлёт `external_update` в WS-комнату → открытые редакторы перечитывают граф **вживую** (юзеру не надо перезаходить). Бот читает активный граф **заново из БД на каждое сообщение** → правка живого PUBLISHED-графа применяется **сразу, без отдельной публикации**.
|
|
69
|
+
- `clone_graph`+`publish_graph` НЕ создают новый живой граф и НЕ песочница: публикация копирует клон (как любой черновик) в id ТЕКУЩЕГО опубликованного графа бота и затирает его содержимое без бэкапа — вместе с живыми правками, сделанными после прошлой публикации; `set_active_graph` на прежний id после этого вернёт уже новое содержимое; открытые редакторы живого графа перечитают его (у кого есть несохранённые правки — получат просьбу обновить страницу). Клон годится, чтобы спокойно собрать крупный рискованный рефактор в черновике, но перед его публикацией сохрани живой граф: `get_graph(graphId, saveToFile)` — откат = залить файл обратно через `edit_graph_live`/`update_graph` с `graphFile`.
|
|
70
|
+
- ⚠️ PUT **активного** (PUBLISHED) графа сервер проверяет как `publish`: валидатор, платные блоки, лимит блоков тарифа, платформа. Ошибка → `HTTP 422` со всеми `code@nodeId`, граф **не сохранён**, бот работает на прежней версии. Черновик (`DRAFT`) PUT сохраняет без проверок (кроме размера: больше 4 МБ → 422 `GRAPH_TOO_LARGE`), и на бота он не влияет: `publish_graph` черновика копирует его в отдельный `PUBLISHED`-граф (`publishedGraphId`), а сам черновик остаётся `DRAFT`. Всё равно прогоняй `validate.mjs` + `dry_run` заранее. Откат: версии активного не хранятся — опубликовать граф «🔙 Авто-бэкап» (или скопировать его содержимое обратно).
|
|
71
|
+
|
|
72
|
+
6. **Отчитайся**: сколько узлов/веток, какие тексты помечены на проверку, ссылка/ id графа.
|
|
73
|
+
|
|
74
|
+
## Важные ограничения
|
|
75
|
+
- **Источник = текст** (этот режим). Если просят распознать с приватной Miro-доски — самый надёжный путь: CSV-экспорт из Miro; либо запуск залогиненного Chrome пользователя и съёмка экрана (headless WebGL-холст Miro не отдаёт). Это отдельный сценарий, не основной для этого скилла.
|
|
76
|
+
- **Авторизация MCP** — персональный токен (создаётся в вебе на `/bots/mcp-tokens`, формат `zmcp_…`, полный доступ). Если инструмент вернул «нет токена» или ошибку доступа — **вызови `setup`**, объясни пользователю шаги, попроси прислать токен и сохрани его через **`set_token`** (применяется сразу, без env/рестарта). Также работают env `RETENSY_MCP_TOKEN` и session-cookie.
|
|
77
|
+
- Бэкенд читает плоские поля `config.text`/`config.photoUrl`; редактор берёт текст из первой карточки `type:"text"`. Поэтому **всегда заполняй и `text`, и `cards`**.
|
|
78
|
+
- **Размер графа ≤ 4 МБ** (JSON `{nodes, edges, canvasMeta}` в UTF-8): больше — `HTTP 422 GRAPH_TOO_LARGE` на `update_graph`/`edit_graph_live`/`patch_graph`/`import_funnel`, и у черновика тоже; граф не сохранён. `validate.mjs` проверяет заранее.
|
|
79
|
+
- **Переменные подписчика:** строка ≤ 65 536 символов (длиннее обрезается), список/объект длиннее не сохраняется; все переменные одного подписчика вместе с именами ≤ 262 144 символов — запись сверх лимита не сохраняется. Большой ответ API не клади в переменную целиком — вытаскивай нужное через `extract`.
|
|
80
|
+
|
|
81
|
+
## Если возможности не хватает — она фиксируется автоматически
|
|
82
|
+
|
|
83
|
+
При каждой неудаче инструмента отправляется **анонимный** отчёт на `POST /api/mcp/report` того же
|
|
84
|
+
сервера retensy (неизвестный инструмент, отказ публикации `errors[]`, ошибка API). Так мы узнаём,
|
|
85
|
+
чего людям не хватает, и добавляем это в продукт.
|
|
86
|
+
|
|
87
|
+
Что это значит для тебя как ассистента:
|
|
88
|
+
- **Не выдумывай обходные пути для того, чего нет в schema.md.** Просят узел/триггер/действие,
|
|
89
|
+
которого в схеме нет, — скажи прямо, что этого пока нет, и что запрос уже зарегистрирован
|
|
90
|
+
автоматически. Не подменяй это самодельным костылём молча.
|
|
91
|
+
- **Не повторяй один и тот же падающий вызов по кругу.** Отчёты дедуплицируются, но бессмысленные
|
|
92
|
+
ретраи всё равно тратят время пользователя. Разбери `code`/`nodeId` и исправь причину.
|
|
93
|
+
- Секреты в отчёт не попадают: токены, cookie и креды интеграций вырезаются, значения аргументов
|
|
94
|
+
по умолчанию скрыты. Пользователь может выключить отчёты через `RETENSY_MCP_TELEMETRY=off`.
|
|
95
|
+
- **Нужна помощь человека** — если что-то не работает или вопрос не про сборку сценария, направляй
|
|
96
|
+
пользователя в чат «Поддержка» в кабинете bots.retensy.com (кнопка внизу бокового меню): там
|
|
97
|
+
отвечает менеджер. Внешние контакты поддержки не называй.
|
|
98
|
+
|
|
99
|
+
## Версия сервера
|
|
100
|
+
|
|
101
|
+
При старте сервер сверяется с npm. Если вышла новая версия — уведомление появится в `setup` и
|
|
102
|
+
в первом ответе инструмента. **Увидел такое уведомление — передай его пользователю**: обновление
|
|
103
|
+
вступает в силу только ПОСЛЕ перезапуска MCP-сервера, сам процесс свой код подменить не может.
|
|
104
|
+
|
|
105
|
+
## Файлы скилла
|
|
106
|
+
- [reference/schema.md](reference/schema.md) — формат графа, типы узлов, конфиги, хэндлы.
|
|
107
|
+
- [reference/validation.md](reference/validation.md) — правила валидатора бэкенда (коды ошибок) и `cardsToLegacy`.
|
|
108
|
+
- `validate.mjs` — оффлайн-проверка графа перед заливкой.
|