@retensy/mcp 0.21.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/README.md CHANGED
@@ -313,6 +313,18 @@ RETENSY_MCP_TOKEN=zmcp_... node src/index.mjs # стартует stdio MCP-с
313
313
  Отдельно: если токен ещё не настроен вовсе, отчёт **не** отправляется — это обычное состояние
314
314
  нового пользователя, а не пробел в возможностях.
315
315
 
316
+ ## Работа партнёра с несколькими проектами
317
+
318
+ Партнёр настраивает MCP **один раз**, своим собственным токеном — дальше тот же токен работает со всеми проектами, доступ к которым ему дали клиенты.
319
+
320
+ - **Токен = аккаунт, не проект.** `zmcp_...` всегда идентифицирует тебя. Он не привязан к конкретному боту или сайту — какие проекты видны и что с ними можно делать, решает не токен, а твоя роль в каждом отдельном проекте.
321
+ - **Доступ — через приглашение, а не передачу токена.** Чтобы ты поработал в боте клиента, клиент сам приглашает тебя соредактором (в боте → «Доступ» → email или ссылка, роль Viewer/Editor/Admin). Свой MCP-токен или пароль клиент никогда не присылает — и ты его не просишь.
322
+ - **`list_bots` и `site_list` показывают всё сразу**: твои собственные проекты и те, куда тебя пригласили — у чужих в ответе есть `ownerName` (имя владельца) и `accessRole` (твоя роль в этом конкретном проекте: OWNER / ADMIN / EDITOR / VIEWER). Так ты сразу видишь, где ты владелец, а где гость с ограниченной ролью.
323
+ - **Передавай `botId`/`siteId` явно** в каждом вызове инструмента — ассистент не хранит «текущий проект». Переключение между проектами клиентов — это просто другой `botId` в следующем вызове, оно не меняет твой аккаунт и не путает данные между проектами.
324
+ - **Доступ проверяется заново на каждый запрос.** Если клиент поменял тебе роль или вовсе убрал из соредакторов, следующий же вызов к этому проекту откажет — без повторной настройки токена и без задержки.
325
+ - **Бери минимально нужную роль.** Viewer — чтобы посмотреть и оценить задачу, Editor — чтобы собирать и публиковать сценарии, Admin — только если по договорённости настраиваешь рассылки или параметры бота.
326
+ - **Никогда не проси пароль от аккаунта клиента или его MCP-токен.** Единственный штатный способ работать в чужом проекте — приглашение с ролью; свой токен отзывай сам в кабинете, если он утёк.
327
+
316
328
  ## Безопасность
317
329
 
318
330
  Токен = доступ к аккаунту по API. Не коммить его; держи в `env`. В конфигах храни ссылку `${RETENSY_MCP_TOKEN}`, не само значение.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retensy/mcp",
3
- "version": "0.21.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": {
package/src/index.mjs CHANGED
@@ -27,7 +27,7 @@ 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.21.0";
30
+ const VERSION = "0.21.1";
31
31
  /** С чего начать пустой сайт (init у /document/ops; на сайте с черновиком игнорируется). */
32
32
  const SITE_INITS = ["starter", "blank", "mini-landing"];
33
33
  /** Безвредная операция, когда нужен только init: бэкенд не принимает пустой ops[]. */
@@ -665,9 +665,9 @@ function instagramAnswer() {
665
665
  }
666
666
 
667
667
  const TOOLS = [
668
- { name: "setup", description: "Показать статус авторизации и пошаговую инструкцию подключения. Вызывай первым, если пользователь не знает, что делать, или при ошибке доступа.", inputSchema: { type: "object", properties: {} } },
669
- { name: "set_token", description: "Сохранить персональный токен (zmcp_...), который пользователь создал на /bots/mcp-tokens. Применяется сразу, без рестарта.", inputSchema: { type: "object", properties: { token: { type: "string", description: "Секрет токена, начинается с zmcp_" } }, required: ["token"] } },
670
- { name: "list_bots", description: "Список ботов пользователя (id, имя, статус).", inputSchema: { type: "object", properties: {} } },
668
+ { name: "setup", description: "Показать статус авторизации и пошаговую инструкцию подключения. Вызывай первым, если пользователь не знает, что делать, или при ошибке доступа. Токен (zmcp_...) определяет АККАУНТ, а не один проект: доступ к каждому боту/сайту — это твоя текущая роль в нём (владелец или приглашённый соредактор), и она проверяется заново при каждом вызове.", inputSchema: { type: "object", properties: {} } },
669
+ { name: "set_token", description: "Сохранить персональный токен (zmcp_...), который пользователь создал на /bots/mcp-tokens. Применяется сразу, без рестарта. Токен определяет аккаунт целиком, не один проект — список доступных проектов (свои + те, куда пригласили) виден в list_bots/site_list. Если владелец проекта уберёт тебя из соредакторов, доступ к этому проекту исчезнет сразу же, без повторной настройки токена.", inputSchema: { type: "object", properties: { token: { type: "string", description: "Секрет токена, начинается с zmcp_" } }, required: ["token"] } },
670
+ { name: "list_bots", description: "Список ботов (id, имя, статус). Включает и свои боты, и боты, в которые тебя пригласили соредактором — у каждого есть accessRole (OWNER/ADMIN/EDITOR/VIEWER); у чужих (не OWNER) есть ownerName — имя владельца проекта. Доступ = твоя текущая роль в проекте, проверяется на каждый вызов; владелец мог отозвать её без предупреждения.", inputSchema: { type: "object", properties: {} } },
671
671
  { name: "list_graphs", description: "Список сценариев САМОГО бота (без узлов). Вебхук-сценарии, которые лишь отвечают через этого бота, сюда не входят — их публикуют в вебе, в «Сценариях» автора.", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
672
672
  { name: "list_channels", description: "Список каналов/групп, подключённых к боту (chatId, title, type, статус бота, дата). chatId — числовой id для условия SUBSCRIBED («Подписан на канал»).", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
673
673
  { name: "integration_catalog", description: "Каталог сервисов Integration Core (GET /api/integrations/catalog): [{provider, name, category, authType, configSchema: [{key, label, hint, secret}] — поля подключения, actions: [{kind, label, inputs}] — действия для сценария и coreDelivery, healthCheck — умеет ли integration_test, inbound: {events: [{key, label, vars: [{key, label, example}], paymentSucceeded}]} | null — провайдер умеет только исходящие действия}]. inbound.events — события для узла TRIGGER_WEBHOOK с заполненным config.provider (см. integration_ingress_url): events[].key идёт в config.event, vars[].key — переменные {{body.<key>}} в сценарии. paymentSucceeded:true — событие сверх обычного вебхука ещё и заводит узел «Оплата прошла» (headless или в чате подписчика, если платёжная ссылка была привязана к нему действием *_link). 404 — Integration Core выключен в сервисе. Read-only.", inputSchema: { type: "object", properties: {} } },
@@ -696,7 +696,7 @@ const TOOLS = [
696
696
  { name: "graph_analytics", description: "Аналитика прохождения сценария по узлам (GET /api/bots/graphs/{graphId}/analytics): сколько пользователей дошло до каждого узла — видно, где отваливается воронка. Read-only.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
697
697
  { 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"] } },
698
698
  { name: "list_links", description: "Стартовые (трекинговые) ссылки бота с UTM (GET /api/bots/{botId}/links): code, метки, число стартов. Это точки входа в воронку. Read-only.", inputSchema: { type: "object", properties: { botId: { type: "string" } }, required: ["botId"] } },
699
- { name: "site_list", description: "Сайты пользователя (раздел «Страницы», GET /api/bots/pages): id, title, mode (BLOCKS — сайт из блоков, CODE — файлы/Mini App), url (основной адрес), publishedRevision, status (ACTIVE | BLOCKED — заблокирован модерацией: посетители видят HTTP 451, удалить такой сайт нельзя — 409) и blockedReason. Read-only.", inputSchema: { type: "object", properties: {} } },
699
+ { name: "site_list", description: "Сайты (раздел «Страницы», GET /api/bots/pages): id, title, mode (BLOCKS — сайт из блоков, CODE — файлы/Mini App), url (основной адрес), publishedRevision, status (ACTIVE | BLOCKED — заблокирован модерацией: посетители видят HTTP 451, удалить такой сайт нельзя — 409) и blockedReason. Включает и свои сайты, и сайты команды проекта (привязанные к боту, где тебя пригласили) — у каждого accessRole (OWNER/ADMIN/EDITOR/VIEWER), у чужих — ownerName. Доступ проверяется на каждый вызов. Read-only.", inputSchema: { type: "object", properties: {} } },
700
700
  { 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). Число сайтов ограничено тарифом (free — 1, «Старт» — 3, «Про» — 10, с «Бизнеса» — без лимита): сверх лимита — HTTP 402 с причиной и upgradeUrl (смена тарифа только в браузере, передай ссылку пользователю). Заблокированный модерацией сайт в site_list имеет status=BLOCKED и blockedReason, посетители видят HTTP 451.", inputSchema: { type: "object", properties: { title: { type: "string" }, slug: { type: "string" }, template: { type: "string", enum: ["starter", "blank", "mini-landing"] } }, required: ["title"] } },
701
701
  { 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"] } },
702
702
  { name: "site_schema", description: "JSON Schema модели сайта (model) и операций правки (ops) — какие блоки и поля бывают (GET /api/bots/pages/schema). Читай перед первой правкой.", inputSchema: { type: "object", properties: {} } },