@retensy/mcp 0.49.0 → 0.50.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retensy/mcp",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
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": {
@@ -42,6 +42,7 @@ description: Собрать воронку (сценарий) бота для с
42
42
  - **Оплата звёздами Telegram (Stars, XTR)** — действие `issue_stars_invoice` внутри `ACTIONS`, ТОЛЬКО для Telegram-ботов (на MAX/VK/Instagram/WEB публикация даст `STARS_TELEGRAM_ONLY`). Кассы и `connectionId` нет: `{ "kind":"issue_stars_invoice", "amount":"150", "title":"Курс", "description":"Доступ", "timeoutMinutes":60 }` (`amount` — целое 1…10000, `title` до 32 знаков). Выходы и правила те же, что у `issue_invoice`: `paid`/`timeout`, счёт последним в блоке, один счёт на блок (ЮKassa и звёзды вместе тоже нельзя). После оплаты в переменных `{{var.stars_charge_id}}` (нужен для возврата), `{{var.stars_amount}}`, `{{var.stars_paid_at}}`; история и возврат — в кабинете «Платежи звёздами» (/bots/stars). Разрешение `pre_checkout_query` включается автоматически при публикации.
43
43
  Счёт обязан быть **последним** действием в блоке и только одним (`INVOICE_NOT_LAST`, `INVOICE_DUPLICATE`).
44
44
  - **`TRIGGER_PAYMENT` («оплата прошла»)** — если счёт выставлял бот, подписчик определяется САМ (счёт помнит его в metadata платежа): можно сразу слать сообщение и вешать метку, разбирать ответ кассы не нужно. Если оплата пришла мимо бота — прогон headless, получателя задавай через `config.target`. Ставь фильтр `minAmount`/`descriptionContains`, иначе сценарий сработает на любую оплату в кассе.
45
+ - **`TRIGGER_ORDER_CREATED` («Заказ создан») / `TRIGGER_ORDER_PAID` («Заказ оплачен»)** — `{siteId?, channel?: "site"|"bot"}`: срабатывает на заказ владельца (оформление / оплата) для каждого опубликованного сценария с этим триггером. Переменные `{{order.number|total|currency|status|name|phone|email|comment|delivery|address|items|channel|id}}`. Типовой кейс — бэкенд сайта: сценарий без бота (WEBHOOK) → `send_email` покупателю, уведомление владельцу, `external_request` в `ACTIONS`.
45
46
  - **`TRIGGER_SCHEDULE` («По расписанию»)** — `{cron:"0 9 * * 1-5", timezone}`: headless-прогон без чата (отправка — только с `config.target`), для сводок, эскалаций, SLA. Журнал — `scenario_runs`.
46
47
  - **Запись на слоты и напоминание**: календарь создаётся `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.
47
48
  - **Переиспользуемые подсценарии — `call_scenario`** (действие в `ACTIONS`, ставь ПОСЛЕДНИМ в блоке): `graphId` — сценарий ЭТОГО ЖЕ бота (`list_graphs`), `mode: "goto"` (по умолчанию, уйти без возврата) или `"call"` (вызвать и вернуться — после `END`/конца цели движок продолжает вызывающего с места после блока). Общая глубина `goto`+`call` в одной цепочке — не больше 5 переходов/вызовов подряд. Коды ошибок и рантайм-поведение — в validation.md.
@@ -111,6 +111,8 @@
111
111
  `{{var.<имя>}}`, всегда `{{var.name}}`/`{{var.email}}`/`{{var.phone}}` (по имени поля, иначе по подписи),
112
112
  UTM `{{var.utm_*}}`. Прогон без чата: узлы отправки — только с `config.target`. Работает и в сценарии
113
113
  без бота (источник WEBHOOK — тогда `TRIGGER_WEBHOOK` не обязателен), и в сценарии бота; не для Instagram.
114
+ - `TRIGGER_ORDER_CREATED` («Заказ создан») — `{ "siteId": "<uuid сайта, необязательно = любой>", "channel": "site"|"bot" }` — заказ создан (оформлен в корзине сайта или в боте). Срабатывает для КАЖДОГО опубликованного сценария владельца с этим триггером, фильтры — `siteId` и `channel` (пусто = без фильтра). Для бэкенда сайта: письмо, уведомление владельцу, `external_request` в `ACTIONS`. Переменные: `{{order.number}}`, `{{order.total}}`, `{{order.currency}}`, `{{order.status}}`, `{{order.name}}`, `{{order.phone}}`, `{{order.email}}`, `{{order.comment}}`, `{{order.delivery}}`, `{{order.address}}`, `{{order.items}}`, `{{order.channel}}`, `{{order.id}}`.
115
+ - `TRIGGER_ORDER_PAID` («Заказ оплачен») — то же, но на оплату заказа; те же `config` и `{{order.*}}`, `{{order.status}}` — статус после оплаты.
114
116
  - `TRIGGER_SCHEDULE` — `{ "cron": "0 9 * * 1-5", "timezone": "Europe/Moscow" }` — запуск сценария по расписанию:
115
117
  cron из 5 полей (минута час день месяц день-недели), не чаще раза в минуту; зона по умолчанию `Europe/Moscow`.
116
118
  Прогон **headless** (без чата): узлы отправки — только с явным `config.target`, действия, которым нужен
@@ -246,6 +246,11 @@ for (const n of nodes) {
246
246
  else if (!UUID_RE.test(String(c.siteId).trim())) errors.push(`SITE_FORM_NO_SITE: ${who} — siteId «${c.siteId}» не UUID сайта (site_list).`);
247
247
  if (c.formId != null && typeof c.formId !== "string") errors.push(`${who}: formId — строка (id формы из site_get) или пусто = любая форма.`);
248
248
  break;
249
+ case "TRIGGER_ORDER_CREATED":
250
+ case "TRIGGER_ORDER_PAID":
251
+ if (c.channel != null && c.channel !== "" && !["site", "bot"].includes(c.channel)) errors.push(`${who}: channel ∈ {site,bot} или пусто.`);
252
+ if (!blank(c.siteId) && !UUID_RE.test(String(c.siteId).trim())) errors.push(`${who}: siteId — UUID сайта (site_list) или пусто = любой.`);
253
+ break;
249
254
  case "TRIGGER_BOOKING": {
250
255
  const EVENTS = ["booked", "confirmed", "cancelled", "reminder", "after_visit"];
251
256
  if (!Array.isArray(c.events) || c.events.length === 0) {
package/src/index.mjs CHANGED
@@ -1265,7 +1265,7 @@ const TOOLS = [
1265
1265
  { name: "site_lead_file_download", description: "Метаданные и ссылка на скачивание файла из заявки сайта (GET /api/bots/pages/{siteId}/leads/{leadId}/files/{fileId}, владелец/ADMIN сайта). Байты файла НЕ передаются через MCP (может быть большим/бинарным) — инструмент делает HEAD-запрос, чтобы подтвердить, что файл принадлежит этой заявке (404 иначе), и вернуть {fileId, fileName, contentType, sizeBytes, downloadUrl}. downloadUrl отдаёт файл только с тем же токеном, что у этого MCP (заголовок Authorization: Bearer zmcp_...), — скачай curl'ом с этим заголовком или открой заявку в кабинете (сайт → «Заявки»). Список файлов заявки с теми же полями уже есть в site_leads (поле files[] каждой заявки) — этот инструмент нужен, только когда нужна именно прямая ссылка на скачивание одного файла.", inputSchema: { type: "object", properties: { siteId: { type: "string" }, leadId: { type: "string" }, fileId: { type: "string" } }, required: ["siteId", "leadId", "fileId"] } },
1266
1266
  // ---- Вебхук-сценарии (/api/scenarios, PAT-доступ открыт решением владельца 14.10) ----
1267
1267
  { name: "scenario_list", description: "Сценарии автора (GET /api/scenarios): [{id, name, status, active, sourceType: BOT|WEBHOOK, botId}] — включая вебхук-сценарии (в list_graphs их нет, см. его описание). sourceType WEBHOOK — свой вход по адресу (path+secret в scenario_get), выход — произвольный бот через scenario_update_source; BOT — живёт в графах привязанного бота (тот же объект, что и в list_graphs/get_graph этого бота). Read-only.", inputSchema: { type: "object", properties: {} } },
1268
- { name: "scenario_create", description: "Создать сценарий (POST /api/scenarios), пустым — узлы/рёбра потом через update_graph/edit_graph_live по id из ответа. botId не передан → вебхук-сценарий (source.type WEBHOOK): сервер сам выпускает путь входа и секрет (видны в scenario_get, ротируются scenario_rotate_secret), выходной бот задаётся отдельно через scenario_update_source. botId передан → бот-сценарий (source.type BOT), сразу привязан к этому боту — нужен доступ на редактирование его графов (owner/EDITOR/ADMIN), иначе 403/404; публикуется он уже через publish_graph, не через scenario_publish. Лимит сценариев тарифа — HTTP 402.", inputSchema: { type: "object", properties: { name: { type: "string" }, botId: { type: "string", description: "UUID бота — делает сценарий бот-сценарием вместо вебхук-сценария" } }, required: ["name"] } },
1268
+ { name: "scenario_create", description: "Создать сценарий (POST /api/scenarios), пустым — узлы/рёбра потом через update_graph/edit_graph_live по id из ответа. botId не передан → вебхук-сценарий (source.type WEBHOOK): сервер сам выпускает путь входа и секрет (видны в scenario_get, ротируются scenario_rotate_secret), выходной бот задаётся отдельно через scenario_update_source. botId передан → бот-сценарий (source.type BOT), сразу привязан к этому боту — нужен доступ на редактирование его графов (owner/EDITOR/ADMIN), иначе 403/404; публикуется он уже через publish_graph, не через scenario_publish. Лимит сценариев тарифа — HTTP 402. Триггеры сценария (узел TRIGGER_*): для заказов владельца — TRIGGER_ORDER_CREATED («Заказ создан») и TRIGGER_ORDER_PAID («Заказ оплачен»), config {siteId?, channel?: \"site\"|\"bot\"}, переменные {{order.*}} (скилл build-bot-funnel).", inputSchema: { type: "object", properties: { name: { type: "string" }, botId: { type: "string", description: "UUID бота — делает сценарий бот-сценарием вместо вебхук-сценария" } }, required: ["name"] } },
1269
1269
  { name: "scenario_get", description: "Сценарий по id (GET /api/scenarios/{id}) — тот же граф, что get_graph, плюс source: {type: BOT|WEBHOOK, config: у WEBHOOK path/secret входа и (если задан) botId выхода, у BOT — только botId} и outputBinding {channel, recipientFrom}. Свой сценарий — всегда; чужой бот-сценарий — если есть доступ к боту (как GET .../graphs/{id}); чужой вебхук-сценарий — 404 (в нём секрет входа, виден только автору).", inputSchema: { type: "object", properties: { scenarioId: { type: "string" } }, required: ["scenarioId"] } },
1270
1270
  { name: "scenario_update_source", description: "Привязка выхода вебхук-сценария, ЦЕЛИКОМ заменяет текущую (PUT /api/scenarios/{id}/source) — только у source.type WEBHOOK, у BOT-сценария 409. botId — бот-выход (нужен доступ на редактирование его графов — owner/EDITOR/ADMIN, иначе 403/404); НЕ передан → привязка снимается, сценарий остаётся headless без бота. channel — OutputChannel: TG_BOT_API (обычный Bot API Telegram/MAX) | TG_USERBOT | IG | MAX; не передан — прежнее значение сохраняется. recipientFrom — откуда брать chatId получателя (например \"CHAT\" — чат-инициатор, или имя переменной сценария); НЕ передан → очищается в пустую строку (поле не частичное — шли его всегда, если хочешь сохранить текущее значение, сначала прочитай scenario_get). У опубликованного сценария новая привязка применяется сразу; если она добавляет НОВЫЕ ошибки проверки (платформа/лимит), которых не было при старой, — HTTP 422 со всеми code@nodeId, ничего не сохранено (уже имевшиеся ошибки не блокируют).", inputSchema: { type: "object", properties: { scenarioId: { type: "string" }, botId: { type: "string", description: "UUID бота-выхода; опусти, чтобы снять привязку" }, channel: { type: "string", enum: ["TG_BOT_API", "TG_USERBOT", "IG", "MAX"] }, recipientFrom: { type: "string", description: "Не передашь — очистится в пустую строку" } }, required: ["scenarioId"] } },
1271
1271
  { name: "scenario_publish", description: "Опубликовать вебхук-сценарий (POST /api/scenarios/{id}/publish) — только source.type WEBHOOK (у BOT-сценария 409, публикуй через publish_graph). Те же проверки, что у publish_graph (узлы/лимиты/платформа выходного бота, если он задан): отказ → HTTP 422 со всеми причинами построчно (code@nodeId: message), ничего не опубликовано.", inputSchema: { type: "object", properties: { scenarioId: { type: "string" } }, required: ["scenarioId"] } },