@retensy/mcp 0.15.0 → 0.15.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.
@@ -6,7 +6,7 @@
6
6
  {
7
7
  "name": "retensy-mcp",
8
8
  "source": "./",
9
- "version": "0.15.0",
9
+ "version": "0.15.1",
10
10
  "description": "Сборка и публикация воронок ботов (Telegram/MAX, Retensy Bots) из описания, рассылки, подключение ботов и сервисов, сайтов из блоков + публикация статей блога в Markdown — через MCP + скилл."
11
11
  }
12
12
  ]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "retensy-mcp",
3
3
  "displayName": "Retensy MCP",
4
- "version": "0.15.0",
4
+ "version": "0.15.1",
5
5
  "description": "MCP-сервер + скилл для Retensy Bots: подключение ботов и сервисов, сборка и публикация воронок ботов (Telegram/MAX), рассылки, сайты из блоков (Zero-блок, дизайны, шаблоны, свои домены, заявки) и статьи блога в Markdown — всё одним токеном zmcp_.",
6
6
  "author": { "name": "retensy", "url": "https://bots.retensy.com" },
7
7
  "homepage": "https://bots.retensy.com/bots",
package/README.md CHANGED
@@ -128,10 +128,10 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
128
128
  | `list_bot_users(botId)` | подписчики/лиды бота (постранично, поиск `query`) |
129
129
  | `list_links(botId)` | стартовые трекинговые ссылки бота с UTM |
130
130
  | `site_list()` | сайты пользователя (id, mode, url, publishedRevision) |
131
- | `site_create(title, slug?)` | новый сайт из блоков → `id` |
131
+ | `site_create(title, slug?, template?)` | новый сайт из блоков → `id`; `template` (`starter`/`blank`/`mini-landing`) — сразу черновик из шаблона |
132
132
  | `site_get(siteId, saveToFile?)` | модель сайта (`revision`, `draft`, `versions[]` публикаций) |
133
133
  | `site_schema()` | JSON Schema модели и операций — читать перед правкой |
134
- | `site_edit(siteId, ops[], revision?, init?)` | правка операциями, всё или ничего: страницы и папки, блоки, Zero-элементы, код блока (`get/set/add_block_code`), дизайны и их кадры, шаблоны (`add_template`), тема, попапы |
134
+ | `site_edit(siteId, ops[]?, revision?, init?)` | правка операциями (`init`: `starter`/`blank`/`mini-landing`; только `init` — черновик из шаблона), всё или ничего: страницы и папки, блоки, Zero-элементы, код блока (`get/set/add_block_code`), дизайны и их кадры, шаблоны (`add_template`), тема, попапы |
135
135
  | `site_templates(category?, full?)` | библиотека шаблонов блоков для `add_template` |
136
136
  | `site_publish(siteId)` | опубликовать черновик → `url` |
137
137
  | `site_rollback(siteId, revision)` | вернуть прошлую публикацию |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retensy/mcp",
3
- "version": "0.15.0",
3
+ "version": "0.15.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",
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",
25
25
  "validate": "node skills/build-bot-funnel/validate.mjs"
26
26
  },
27
27
  "engines": {
@@ -14,8 +14,10 @@ description: Собрать сайт или лендинг из блоков в
14
14
  1. `site_schema` — какие блоки и поля бывают (`model`) и какие операции есть (`ops`); `site_templates` — готовые
15
15
  секции из библиотеки.
16
16
  2. `site_create {title}` → `id`. Для правки существующего — `site_list`, `site_get {siteId}`.
17
- 3. Первый `site_edit` с `init`: `starter` — готовый лендинг (шапка, обложка, текст, преимущества, форма, подвал) или
18
- `blank` — пустая главная. Заполни тексты, не выдумывай факты о бизнесе — спрашивай.
17
+ 3. Первый `site_edit` с `init`: `starter` — готовый лендинг (шапка, обложка, текст, преимущества, форма, подвал),
18
+ `blank` — пустая главная или `mini-landing` — мини-лендинг «как Taplink» (см. ниже). Только `init` без `ops` —
19
+ создать черновик из шаблона; то же сразу при создании — `site_create {title, template}`. На сайте с черновиком
20
+ `init` игнорируется. Заполни тексты, не выдумывай факты о бизнесе — спрашивай.
19
21
  4. Картинки — `site_upload_asset {siteId, path|url}` → `assets/…` в поля `image`, `logo`, `icon`, `style.bg.image`.
20
22
  5. `site_get` — проверь модель, `site_publish` — сайт открыт по `url`.
21
23
  6. После публикации: заявки — `site_leads`, куда их слать — `site_lead_settings` (бот уведомлений, почта, вебхук,
@@ -51,6 +53,21 @@ description: Собрать сайт или лендинг из блоков в
51
53
  поправь и верни `set_block_code {blockId, code}`; новый блок из кода — `add_block_code {container, code}`. У
52
54
  обычных блоков код — JSON `{type, variant, props, style}`. Тот же код пользователь видит во вкладке «Код» редактора.
53
55
 
56
+ ## Мини-лендинг и блок «Кнопки мессенджеров»
57
+
58
+ `init: "mini-landing"` (или `site_create {template: "mini-landing"}`) — одна страница без шапки и подвала, колонка
59
+ 640px, на весь экран блок `type: "messengers"`. Его `props`:
60
+
61
+ - `avatar?` (`assets/…`), `title?`, `text?`;
62
+ - `bots: [{botId, label?, start?}]` — боты владельца по порядку, кнопки собираются при публикации; пусто — все
63
+ активные боты (Telegram, MAX, Instagram). `start` — метка старта (латиница, цифры, `_`, `-`);
64
+ - `links: [{label, action, style: primary|secondary|link}]` — свои кнопки (до 10): `action` —
65
+ `{kind:"url", href}` (только http(s), mailto:, tel:, относительные и `#якорь`; `javascript:`/`data:`/`vbscript:` бэкенд отклоняет),
66
+ `{kind:"phone", phone}`, `{kind:"email", email}`, `{kind:"page"|"anchor"|"popup"|"bot", …}`;
67
+ - `colors: "brand"` (цвета мессенджеров) | `"theme"` (основной цвет темы).
68
+
69
+ Блок ставится и на обычный сайт: `add_block {container, type: "messengers"}`.
70
+
54
71
  ## Папки, дизайны, шаблоны
55
72
 
56
73
  - Папки страниц (для навигации в кабинете): `add_folder {name}` → id в `results`; `rename_folder {folderId, name}`,
package/src/index.mjs CHANGED
@@ -27,7 +27,11 @@ import os from "node:os";
27
27
  import fs from "node:fs";
28
28
  import path from "node:path";
29
29
 
30
- const VERSION = "0.15.0";
30
+ const VERSION = "0.15.1";
31
+ /** С чего начать пустой сайт (init у /document/ops; на сайте с черновиком игнорируется). */
32
+ const SITE_INITS = ["starter", "blank", "mini-landing"];
33
+ /** Безвредная операция, когда нужен только init: бэкенд не принимает пустой ops[]. */
34
+ const SITE_NOOP_OPS = [{ op: "set_settings", settings: {} }];
31
35
  const PKG_NAME = "@retensy/mcp";
32
36
  const BASE = (process.env.RETENSY_BASE_URL || "https://bots.retensy.com").replace(/\/+$/, "");
33
37
  const CONFIG_DIR = path.join(os.homedir(), ".retensy-bot-graph");
@@ -614,7 +618,7 @@ const TOOLS = [
614
618
  { name: "list_bot_users", description: "Пользователи (подписчики/лиды) бота, постранично (GET /api/bots/{botId}/users). Опц. page (с 0), size (по умолч. 25), query (поиск по имени/username/id). Read-only.", inputSchema: { type: "object", properties: { botId: { type: "string" }, page: { type: "number" }, size: { type: "number" }, query: { type: "string" } }, required: ["botId"] } },
615
619
  { name: "list_links", description: "Стартовые (трекинговые) ссылки бота с UTM (GET /api/bots/{botId}/links): code, метки, число стартов. Это точки входа в воронку. Read-only.", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
616
620
  { name: "site_list", description: "Сайты пользователя (раздел «Страницы», GET /api/bots/pages): id, title, mode (BLOCKS — сайт из блоков, CODE — файлы/Mini App), url (основной адрес), publishedRevision. Read-only.", inputSchema: { type: "object", properties: {} } },
617
- { name: "site_create", description: "Создать сайт из блоков (POST /api/bots/pages, mode=BLOCKS). Возвращает id. Дальше: site_edit (init=starter — стартовый лендинг, init=blank — пустая главная) → site_publish. slug — «название» в адресе pages.retensy.com/<id>/<slug>/ (необязательно, по умолчанию транслит title).", inputSchema: { type: "object", properties: { title: { type: "string" }, slug: { type: "string" } }, required: ["title"] } },
621
+ { name: "site_create", description: "Создать сайт из блоков (POST /api/bots/pages, mode=BLOCKS). Возвращает id. Дальше: site_edit (init=starter — стартовый лендинг, init=blank — пустая главная, init=mini-landing — мини-лендинг с кнопками мессенджеров) → site_publish. template (starter|blank|mini-landing) — сразу создать черновик из шаблона (как «Мини-лендинг» в редакторе), в ответе revision. slug — «название» в адресе pages.retensy.com/<id>/<slug>/ (необязательно, по умолчанию транслит title).", inputSchema: { type: "object", properties: { title: { type: "string" }, slug: { type: "string" }, template: { type: "string", enum: ["starter", "blank", "mini-landing"] } }, required: ["title"] } },
618
622
  { name: "site_get", description: "Модель сайта из блоков (GET /api/bots/pages/{siteId}/document): revision, draft (SiteModel: theme, globals.header/footer, pages[].blocks[], popups[]) — id страниц/блоков/попапов нужны для site_edit. draft=null — сайт пуст (первый site_edit создаст его). saveToFile — записать модель на диск и вернуть путь.", inputSchema: { type: "object", properties: { siteId: { type: "string" }, saveToFile: { type: "string" } }, required: ["siteId"] } },
619
623
  { name: "site_schema", description: "JSON Schema модели сайта (model) и операций правки (ops) — какие блоки и поля бывают (GET /api/bots/pages/schema). Читай перед первой правкой.", inputSchema: { type: "object", properties: {} } },
620
624
  { name: "site_edit", description: "Правка сайта операциями — всё или ничего (POST /api/bots/pages/{siteId}/document/ops). " +
@@ -626,9 +630,9 @@ const TOOLS = [
626
630
  "Код блока: get_block_code{blockId} (results[i].code: Zero — разметка <zero>…</zero>, остальные — JSON) · set_block_code{blockId,code} · add_block_code{container,code,after?}. " +
627
631
  "Zero-блок (type zero, свободная вёрстка как в Tilda): add_element{blockId, kind: text|image|button|shape|video|html|group, frame?{d:{x,y,w,h,container?,axisX?,axisY?}, t?, m?}, props?, style?, hover?, anim?, link?, parent?, name?, fixed?} · update_element{blockId,elementId, …те же поля, hidden?, locked?, link:null — убрать} · remove_element · move_element{delta: +1 — слой выше} · group_elements{blockId,elementIds[],name?} · ungroup_element. " +
628
632
  "Сайт: set_global{slot: header|footer, on} · set_theme{theme} · set_settings{settings} · add_popup{name} · update_popup{popupId,name?,width?} · remove_popup{popupId}. " +
629
- "props/style/theme/frame — JSON Merge Patch (null удаляет ключ, массивы заменяются целиком). Типы блоков: header, cover, text, image, gallery, buttons, features, form, video, html, spacer, footer, zero. " +
630
- "Значения по экранам: {d, t?, m?} (десктоп/планшет/телефон). revision — защита от перезаписи (409, если сайт изменили); init (starter|blank) — с чего начать пустой сайт. " +
631
- "Ответ: новая revision и results[] с id созданного (и code у get_block_code). Ошибки — HTTP 422 с путями. Тариф: HTML-блок и HTML-элемент Zero публикуются только на платном тарифе (422 при site_publish).", inputSchema: { type: "object", properties: { siteId: { type: "string" }, ops: { type: "array", items: { type: "object" } }, revision: { type: "number" }, init: { type: "string", enum: ["starter", "blank"] } }, required: ["siteId", "ops"] } },
633
+ "props/style/theme/frame — JSON Merge Patch (null удаляет ключ, массивы заменяются целиком). Типы блоков: header, cover, text, image, gallery, buttons, features, form, video, html, spacer, footer, zero, messengers. " +
634
+ "Значения по экранам: {d, t?, m?} (десктоп/планшет/телефон). revision — защита от перезаписи (409, если сайт изменили); init (starter|blank|mini-landing) — с чего начать пустой сайт (на сайте с черновиком игнорируется); только init без ops — создать черновик из шаблона. " +
635
+ "Ответ: новая revision и results[] с id созданного (и code у get_block_code). Ошибки — HTTP 422 с путями. Тариф: HTML-блок и HTML-элемент Zero публикуются только на платном тарифе (422 при site_publish).", inputSchema: { type: "object", properties: { siteId: { type: "string" }, ops: { type: "array", items: { type: "object" } }, revision: { type: "number" }, init: { type: "string", enum: ["starter", "blank", "mini-landing"] } }, required: ["siteId"] } },
632
636
  { name: "site_publish", description: "Опубликовать черновик сайта (POST /api/bots/pages/{siteId}/publish): рендер в статику, адрес начинает отдавать новую версию. Ошибки проверки — HTTP 422 с путями. Возвращает publishedRevision и url.", inputSchema: { type: "object", properties: { siteId: { type: "string" } }, required: ["siteId"] } },
633
637
  { name: "site_upload_asset", description: "Загрузить картинку/видео в сайт (POST /api/bots/pages/{siteId}/upload, папка assets). Передай path (локальный файл) ИЛИ url. Возвращает asset — строку вида assets/<имя> для полей image/logo/icon/style.bg.image.", inputSchema: { type: "object", properties: { siteId: { type: "string" }, path: { type: "string" }, url: { type: "string" } }, required: ["siteId"] } },
634
638
  { name: "site_rollback", description: "Вернуть прошлую публикацию сайта (POST /api/bots/pages/{siteId}/publish/rollback): revision — номер из истории публикаций (site_get → versions[]). Черновик заменяется этой версией и сразу публикуется. Возвращает publishedRevision и url.", inputSchema: { type: "object", properties: { siteId: { type: "string" }, revision: { type: "number" } }, required: ["siteId", "revision"] } },
@@ -882,7 +886,12 @@ async function handleCall(params) {
882
886
  case "site_list": return okResult(await api("/api/bots/pages"));
883
887
  case "site_create": {
884
888
  if (!a.title) throw new Error("Передай title сайта.");
885
- return okResult(await api("/api/bots/pages", { method: "POST", body: { title: a.title, slug: a.slug || undefined, mode: "BLOCKS" } }));
889
+ if (a.template != null && !SITE_INITS.includes(a.template)) throw new Error(`template — одно из: ${SITE_INITS.join(", ")}.`);
890
+ const page = await api("/api/bots/pages", { method: "POST", body: { title: a.title, slug: a.slug || undefined, mode: "BLOCKS" } });
891
+ if (!a.template) return okResult(page);
892
+ // черновик сразу, как редактор при «Мини-лендинг»: бэкенд не берёт пустой ops[] — безвредная set_settings{}
893
+ const doc = await api(`/api/bots/pages/${page.id}/document/ops`, { method: "POST", body: { ops: SITE_NOOP_OPS, init: a.template } });
894
+ return okResult({ ...page, template: a.template, revision: doc?.revision });
886
895
  }
887
896
  case "site_get": {
888
897
  const doc = await api(`/api/bots/pages/${a.siteId}/document`);
@@ -895,8 +904,11 @@ async function handleCall(params) {
895
904
  }
896
905
  case "site_schema": return okResult(await api("/api/bots/pages/schema"));
897
906
  case "site_edit": {
898
- if (!Array.isArray(a.ops) || !a.ops.length) throw new Error("Передай ops — массив операций (см. site_schema).");
899
- return okResult(await api(`/api/bots/pages/${a.siteId}/document/ops`, { method: "POST", body: { ops: a.ops, revision: a.revision, init: a.init } }));
907
+ const hasOps = Array.isArray(a.ops) && a.ops.length > 0;
908
+ if (!hasOps && !a.init) throw new Error("Передай ops — массив операций (см. site_schema) — или init, чтобы только создать черновик.");
909
+ if (a.init != null && !SITE_INITS.includes(a.init)) throw new Error(`init — одно из: ${SITE_INITS.join(", ")}.`);
910
+ const ops = hasOps ? a.ops : SITE_NOOP_OPS; // бэкенд отвечает 422 на пустой ops[]
911
+ return okResult(await api(`/api/bots/pages/${a.siteId}/document/ops`, { method: "POST", body: { ops, revision: a.revision, init: a.init } }));
900
912
  }
901
913
  case "site_publish": {
902
914
  const r = await api(`/api/bots/pages/${a.siteId}/publish`, { method: "POST" });