zaytsv-bot-graph-mcp 0.4.3 → 0.5.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": "zaytsv-bot-graph-mcp",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "MCP server to build and publish Telegram bot funnels in the zaytsv /bots service. Zero dependencies.",
5
5
  "type": "module",
6
6
  "bin": { "zaytsv-bot-graph-mcp": "src/index.mjs" },
@@ -106,6 +106,21 @@
106
106
 
107
107
  > **Платформа MAX.** Боты конструктора умеют работать и в мессенджере MAX. Там не поддерживаются `SUBSCRIBED`/`NOT_SUBSCRIBED` (нет членства в каналах) и reply-клавиатуры; публикация такого графа на MAX-бот вернёт **мягкие предупреждения** (не блокирует). Для Telegram-ботов всё работает как описано.
108
108
 
109
+ ## Кросс-платформенное копирование (Telegram ⇄ MAX)
110
+
111
+ Инструмент `copy_graph` копирует граф в **другого бота** пользователя (`targetBotId`), в т.ч. на другую платформу. Формат графа один и тот же; различается лишь платформа бота-получателя. При копировании в MAX-бот несовместимые узлы **адаптируются**, отчёт — в `notes[]`:
112
+
113
+ | code | severity | что значит |
114
+ |---|---|---|
115
+ | `MAX_CONTACT_AS_TEXT` | TRANSFORM | вопрос с `inputKind=CONTACT` переписан в `inputKind=TEXT` + `validator=PHONE` (в MAX нет кнопки «поделиться контактом»; иначе узел стал бы тупиком) |
116
+ | `MAX_SUBSCRIBED_ALWAYS_NO` | MANUAL | условие `SUBSCRIBED` оставлено как есть, но в MAX всегда «не подписан» — проверьте ветвление вручную |
117
+ | `MAX_VOICE_AS_AUDIO` | INFO | голосовое уйдёт обычным аудио |
118
+ | `MAX_VIDEO_NOTE_AS_VIDEO` | INFO | кружок уйдёт обычным видео |
119
+ | `MAX_GALLERY_AS_ATTACHMENTS` | INFO | галерея уйдёт одним сообщением с вложениями |
120
+ | `MAX_DELETE_NOOP` | INFO | действие «удалить сообщение» в MAX игнорируется |
121
+
122
+ `preview: true` возвращает только `notes[]` (без копирования). Копирование в Telegram-бота (или в бота той же платформы) — точная копия, `notes[]` пустой. Новый граф создаётся как `DRAFT` с именем «… (copy)». Копировать в тот же бот нельзя (для дублирования — `clone_graph`).
123
+
109
124
  ## Выходные хэндлы (`sourceHandle`) — шпаргалка
110
125
  | Узел | Хэндлы |
111
126
  |---|---|
package/src/index.mjs CHANGED
@@ -21,7 +21,7 @@ import os from "node:os";
21
21
  import fs from "node:fs";
22
22
  import path from "node:path";
23
23
 
24
- const VERSION = "0.4.3";
24
+ const VERSION = "0.5.0";
25
25
  const BASE = (process.env.ZAYTSV_BASE_URL || "https://zaytsv.ru").replace(/\/+$/, "");
26
26
  const CONFIG_DIR = path.join(os.homedir(), ".zaytsv-bot-graph");
27
27
  const TOKEN_FILE = path.join(CONFIG_DIR, "token");
@@ -114,6 +114,7 @@ const TOOLS = [
114
114
  { name: "create_graph_from_template", description: "Создать граф (DRAFT) из шаблона (см. list_templates). Возвращает граф с id — дальше правь через update_graph.", inputSchema: { type: "object", properties: { botId: { type: "string" }, templateId: { type: "string" }, name: { type: "string" } }, required: ["botId", "templateId"] } },
115
115
  { name: "rename_graph", description: "Переименовать сценарий (работает и для опубликованных — имя не влияет на исполнение).", inputSchema: { type: "object", properties: { graphId: { type: "string" }, name: { type: "string" } }, required: ["graphId", "name"] } },
116
116
  { name: "clone_graph", description: "Склонировать граф в новый DRAFT «… (copy)» — безопасно итерировать поверх опубликованного.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
117
+ { name: "copy_graph", description: "Скопировать граф в ДРУГОГО бота (в т.ч. на другую платформу Telegram⇄MAX). Возвращает {graphId, sourcePlatform, targetPlatform, notes[]}. notes[] помечают, что адаптировано (severity=TRANSFORM, напр. вопрос-контакт → ввод телефона текстом), что требует ручной правки (MANUAL, напр. условие SUBSCRIBED в MAX) и особенности платформы (INFO). preview=true — только проверка совместимости, без копирования. Тот же бот запрещён (для дублирования есть clone_graph).", inputSchema: { type: "object", properties: { graphId: { type: "string" }, targetBotId: { type: "string", description: "id бота-получателя (см. list_bots)" }, preview: { type: "boolean", description: "true = только отчёт о совместимости, ничего не сохраняется" } }, required: ["graphId", "targetBotId"] } },
117
118
  { name: "delete_graph", description: "Удалить граф. Активный (опубликованный и назначенный боту) удалить нельзя — будет 409; сначала переключи активный через set_active_graph.", inputSchema: { type: "object", properties: { graphId: { type: "string" } }, required: ["graphId"] } },
118
119
  { name: "set_active_graph", description: "Назначить, какой опубликованный граф активен у бота (переключение живого сценария без перепубликации).", inputSchema: { type: "object", properties: { botId: { type: "string" }, graphId: { type: "string" } }, required: ["botId", "graphId"] } },
119
120
  ];
@@ -214,6 +215,8 @@ async function handleCall(params) {
214
215
  return okResult(await api(`/api/tg/graphs/${a.graphId}/rename`, { method: "PATCH", body: { name: a.name } }));
215
216
  case "clone_graph":
216
217
  return okResult(await api(`/api/tg/graphs/${a.graphId}/clone`, { method: "POST" }));
218
+ case "copy_graph":
219
+ return okResult(await api(`/api/tg/graphs/${a.graphId}/copy`, { method: "POST", body: { targetBotId: a.targetBotId, preview: a.preview === true } }));
217
220
  case "delete_graph":
218
221
  await api(`/api/tg/graphs/${a.graphId}`, { method: "DELETE" });
219
222
  return okResult(`🗑️ Граф ${a.graphId} удалён.`);