@retensy/mcp 0.34.0 → 0.35.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/README.md +8 -2
- package/package.json +2 -2
- package/src/agent-tools.mjs +88 -0
- package/src/index.mjs +18 -3
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
57
57
|
- 📰 **Публикации и VK**: `publications_usage` — месячная квота (общая для Telegram/MAX/Instagram/VK), `vk_status`/`vk_connect`/`vk_disconnect`/`vk_post` — подключение VK-сообщества токеном и публикация на стену.
|
|
58
58
|
- 🔗 **Ссылка вместо отказа**: что нельзя сделать через API (вход через Google/Instagram, оплата тарифа, вход в аккаунт) — инструмент возвращает прямую ссылку и одну строку, что сделать.
|
|
59
59
|
- 📝 **Статьи блога** (тот же токен `zmcp_…`): `article_publish`, `article_update`, `article_list`, `article_get` — публикация статей в Markdown (как README на GitHub) в раздел **/articles**.
|
|
60
|
-
- 🧠 **ИИ-агенты и база знаний**: `agent_list`/`agent_get`/`agent_create`/`agent_update`/`agent_publish`/`agent_health`/`agent_test_chat`/`agent_unanswered`, `kb_docs`/`kb_add_qa`/`kb_add_text`/`kb_add_site`/`kb_reindex` — настройка ИИ-агента и его базы знаний (раздел «ИИ-агенты»).
|
|
60
|
+
- 🧠 **ИИ-агенты и база знаний**: `agent_list`/`agent_get`/`agent_create`/`agent_update`/`agent_publish`/`agent_health`/`agent_test_chat`/`agent_unanswered`, `agent_tool_calls`, `kb_docs`/`kb_add_qa`/`kb_add_text`/`kb_add_site`/`kb_reindex` — настройка ИИ-агента и его базы знаний (раздел «ИИ-агенты»).
|
|
61
61
|
- 📎 **Медиа**: `upload_file` грузит фото/видео/документы в библиотеку **/bots/files** (до 50 МБ) и возвращает публичный URL — его вставляешь в медиа-карточку сценария.
|
|
62
62
|
- 🌐 **Сайты из блоков** (раздел «Страницы»): создание, правка операциями (блоки, Zero-блок со свободной вёрсткой, код блока, папки страниц, дизайны, шаблоны из библиотеки), публикация и откат, свои домены, заявки из форм и куда их доставлять — инструменты `site_*` + скилл `build-site`.
|
|
63
63
|
- 🧠 **Скилл `build-bot-funnel`**: учит агента собирать корректный граф (типы узлов, ветки, кнопки, задержки) и проверять его перед публикацией. Поддерживает Telegram, MAX и Instagram.
|
|
@@ -319,7 +319,8 @@ MCP-сервер (+ скилл для Claude Code) для **сборки и пу
|
|
|
319
319
|
| `site_analytics_events(siteId, from?, to?, page?, size?)` | сырые события сайта постранично (`PAGE_VIEW`\|`CLICK`\|`FORM_SUBMIT`, UTM) |
|
|
320
320
|
| `bot_subscriber_analytics(botId, from, to)` | новые подписчики/активность бота по дням (дашборд аналитики, диапазон до 92 дней) |
|
|
321
321
|
| `scenario_timeseries(graphId, from, to)` | дневной ряд прогонов сценария `{ok, failed, other}` (по узлам — `graph_analytics`) |
|
|
322
|
-
| `agent_tool_test(agentId, toolId, params?)` | проверить инструмент агента
|
|
322
|
+
| `agent_tool_test(agentId, toolId, params?, live?)` | проверить инструмент агента напрямую, без модели — бюджет ИИ не тратится. Чтение: `AMO_LOOKUP`/`SHEETS_LOOKUP`/`HTTP_API`. Запись (`AMO_CREATE_LEAD`, `AMO_UPDATE_LEAD`, `AMO_UPDATE_CONTACT`, `SHEETS_APPEND`, `HTTP_POST`, `SET_FIELD`, `ADD_TAG`, `HANDOFF`): по умолчанию песочница (ничего не уходит вовне), `live:true` отправляет по-настоящему |
|
|
323
|
+
| `agent_tool_calls(agentId, toolId?, limit?)` | журнал вызовов инструментов записи агента: кто/когда/аргументы без секретов/результат |
|
|
323
324
|
| `site_lead_file_download(siteId, leadId, fileId)` | метаданные вложения заявки + ссылка на скачивание (сам файл через MCP не передаётся — HEAD-запрос, не GET) |
|
|
324
325
|
| `scenario_list()` | сценарии автора, включая вебхук-сценарии (`sourceType: WEBHOOK`\|`BOT`) — в `list_graphs` вебхук-сценарии не входят |
|
|
325
326
|
| `scenario_create(name, botId?)` | создать сценарий: без `botId` — вебхук-сценарий (сервер сам выпускает путь входа и секрет), с `botId` — бот-сценарий, сразу привязанный к боту |
|
|
@@ -481,3 +482,8 @@ RETENSY_MCP_TOKEN=zmcp_... node src/index.mjs # стартует stdio MCP-с
|
|
|
481
482
|
## Лицензия
|
|
482
483
|
|
|
483
484
|
MIT — см. [LICENSE](LICENSE).
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
## Инструменты агента на запись
|
|
488
|
+
|
|
489
|
+
`agent_update {patch:{tools:[...]}}` включает агенту инструменты записи по одному: создать/обновить сделку и контакт в amoCRM (через подключение из «Подключений»), добавить строку в Google Таблицу, POST на свой API (SSRF-защита, без редиректов), сохранить поле/тег подписчика, передать оператору. У каждого — JSON-схема аргументов (`argsSchema`), шаблоны `{аргумент}`, подтверждение у пользователя (`confirmBeforeRun`, по умолчанию включено для внешних записей), лимиты на диалог и сутки, идемпотентность. Список проверяется локально до отправки. Проверка — `agent_tool_test` (песочница по умолчанию), журнал — `agent_tool_calls`. Не работает в публичном веб-чате агента.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retensy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.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": {
|
|
@@ -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 && node scripts/call-scenario.test.mjs && node scripts/audience-publications.test.mjs && node scripts/chains-analytics.test.mjs && node scripts/platform-decisions.test.mjs && node scripts/token-scopes.test.mjs && node scripts/secondary-graphs.test.mjs && node scripts/graph-versions.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/agent-write-tools.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 && node scripts/audience-publications.test.mjs && node scripts/chains-analytics.test.mjs && node scripts/platform-decisions.test.mjs && node scripts/token-scopes.test.mjs && node scripts/secondary-graphs.test.mjs && node scripts/graph-versions.test.mjs",
|
|
25
25
|
"validate": "node skills/build-bot-funnel/validate.mjs"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Проверка tools[] агента до отправки в agent_update: зеркало AiAgentService.tools + AgentWriteToolConfig
|
|
2
|
+
// на бэкенде (не строже его — иначе MCP отвергнет то, что сервер принял бы). Возвращает список ошибок.
|
|
3
|
+
|
|
4
|
+
const READ = ["AMO_LOOKUP", "SHEETS_LOOKUP", "HTTP_API"];
|
|
5
|
+
const WRITE = ["AMO_CREATE_LEAD", "AMO_UPDATE_LEAD", "AMO_UPDATE_CONTACT", "SHEETS_APPEND", "HTTP_POST", "SET_FIELD", "ADD_TAG", "HANDOFF"];
|
|
6
|
+
const ARG_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,39}$/;
|
|
7
|
+
const PROP_KEYS = new Set(["type", "description", "enum", "pattern", "maxLength", "minLength", "minimum", "maximum"]);
|
|
8
|
+
const SCHEMA_KEYS = new Set(["type", "properties", "required", "description"]);
|
|
9
|
+
const FORBIDDEN_HEADERS = new Set(["host", "content-length", "transfer-encoding", "connection", "content-type", "expect", "upgrade", "te", "trailer"]);
|
|
10
|
+
const MAX_TOOLS = 10;
|
|
11
|
+
|
|
12
|
+
export const AGENT_WRITE_TOOL_TYPES = WRITE;
|
|
13
|
+
|
|
14
|
+
function schemaErrors(schema, errs, id) {
|
|
15
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return errs.push(`${id}: argsSchema — объект {properties, required}`), new Set();
|
|
16
|
+
if (schema.type != null && schema.type !== "object") errs.push(`${id}: argsSchema.type должен быть object`);
|
|
17
|
+
for (const k of Object.keys(schema)) if (!SCHEMA_KEYS.has(k)) errs.push(`${id}: ключевое слово «${k}» в argsSchema не поддерживается`);
|
|
18
|
+
const props = schema.properties;
|
|
19
|
+
if (!props || typeof props !== "object" || Array.isArray(props) || Object.keys(props).length === 0) {
|
|
20
|
+
errs.push(`${id}: опишите хотя бы один аргумент в argsSchema.properties`);
|
|
21
|
+
return new Set();
|
|
22
|
+
}
|
|
23
|
+
if (Object.keys(props).length > 10) errs.push(`${id}: не больше 10 аргументов`);
|
|
24
|
+
for (const [name, p] of Object.entries(props)) {
|
|
25
|
+
if (!ARG_NAME.test(name)) errs.push(`${id}: имя аргумента «${name}» — латиница, цифры, подчёркивание`);
|
|
26
|
+
if (!p || typeof p !== "object") { errs.push(`${id}: описание аргумента ${name} — объект`); continue; }
|
|
27
|
+
for (const k of Object.keys(p)) if (!PROP_KEYS.has(k)) errs.push(`${id}: ключевое слово «${k}» у аргумента ${name} не поддерживается`);
|
|
28
|
+
if (!["string", "number", "integer", "boolean"].includes(p.type)) errs.push(`${id}: type аргумента ${name} — string, number, integer или boolean`);
|
|
29
|
+
if (p.enum != null && (!Array.isArray(p.enum) || p.enum.length === 0 || p.enum.length > 30)) errs.push(`${id}: enum аргумента ${name} — от 1 до 30 значений`);
|
|
30
|
+
if (p.pattern != null) {
|
|
31
|
+
try { new RegExp(p.pattern); } catch { errs.push(`${id}: pattern аргумента ${name} не разбирается`); }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
for (const r of schema.required || []) if (!(r in props)) errs.push(`${id}: required содержит неизвестный аргумент ${r}`);
|
|
35
|
+
return new Set(Object.keys(props));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function placeholders(tpl, known, errs, id) {
|
|
39
|
+
for (const m of String(tpl ?? "").matchAll(/\{([A-Za-z_][A-Za-z0-9_]{0,39})\}/g)) {
|
|
40
|
+
if (!known.has(m[1]) && m[1] !== "now") errs.push(`${id}: в шаблоне {${m[1]}} нет в argsSchema.properties`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function validateAgentTools(tools) {
|
|
45
|
+
const errs = [];
|
|
46
|
+
if (!Array.isArray(tools)) return ["tools должен быть массивом"];
|
|
47
|
+
if (tools.length > MAX_TOOLS) errs.push(`не больше ${MAX_TOOLS} инструментов`);
|
|
48
|
+
const ids = new Set();
|
|
49
|
+
for (const t of tools) {
|
|
50
|
+
const id = t?.id || "?";
|
|
51
|
+
if (!t || typeof t !== "object") { errs.push("инструмент — объект"); continue; }
|
|
52
|
+
if (!/^[a-z0-9_]{1,40}$/.test(t.id || "")) errs.push(`${id}: id — a-z, 0-9, _ (до 40)`);
|
|
53
|
+
if (ids.has(t.id)) errs.push(`${id}: id повторяется`);
|
|
54
|
+
ids.add(t.id);
|
|
55
|
+
if (!t.title) errs.push(`${id}: нужен title`);
|
|
56
|
+
if (![...READ, ...WRITE].includes(t.type)) { errs.push(`${id}: неизвестный type ${t.type}`); continue; }
|
|
57
|
+
if (!WRITE.includes(t.type)) continue;
|
|
58
|
+
for (const [k, max] of [["limitPerConversation", 20], ["limitPerDay", 1000]]) {
|
|
59
|
+
if (t[k] != null && !(Number.isInteger(t[k]) && t[k] >= 1 && t[k] <= max)) errs.push(`${id}: ${k} — число от 1 до ${max}`);
|
|
60
|
+
}
|
|
61
|
+
let known = new Set();
|
|
62
|
+
if (!["SET_FIELD", "ADD_TAG", "HANDOFF"].includes(t.type)) known = schemaErrors(t.argsSchema, errs, id);
|
|
63
|
+
if (t.type.startsWith("AMO_") && !t.amoConnectionId) errs.push(`${id}: нужен amoConnectionId (подключение amoCRM)`);
|
|
64
|
+
if (t.type === "AMO_CREATE_LEAD" && !t.amoLeadName) errs.push(`${id}: нужен amoLeadName`);
|
|
65
|
+
if (t.type === "AMO_UPDATE_LEAD" && !t.amoLeadId) errs.push(`${id}: нужен amoLeadId`);
|
|
66
|
+
if (t.type === "AMO_UPDATE_CONTACT" && !t.amoContactId) errs.push(`${id}: нужен amoContactId`);
|
|
67
|
+
if (t.type === "SHEETS_APPEND") {
|
|
68
|
+
if (!t.sheetsEmail || !t.sheetsSpreadsheetId) errs.push(`${id}: нужны sheetsEmail и sheetsSpreadsheetId`);
|
|
69
|
+
if (!Array.isArray(t.sheetsRowTemplate) || t.sheetsRowTemplate.length === 0) errs.push(`${id}: нужен sheetsRowTemplate (ячейки)`);
|
|
70
|
+
}
|
|
71
|
+
if (t.type === "HTTP_POST") {
|
|
72
|
+
if (!/^https?:\/\//i.test(t.httpUrlTemplate || "")) errs.push(`${id}: httpUrlTemplate — http:// или https://`);
|
|
73
|
+
if (t.httpBodyTemplate) {
|
|
74
|
+
try { JSON.parse(t.httpBodyTemplate); } catch { errs.push(`${id}: httpBodyTemplate — невалидный JSON`); }
|
|
75
|
+
}
|
|
76
|
+
for (const h of t.httpHeaders || []) {
|
|
77
|
+
if (!/^[A-Za-z][A-Za-z0-9-]{0,39}$/.test(h?.name || "") || FORBIDDEN_HEADERS.has(String(h.name).toLowerCase())) errs.push(`${id}: недопустимый заголовок ${h?.name}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (t.type === "SET_FIELD" && !(t.allowedFields || []).length) errs.push(`${id}: нужен allowedFields`);
|
|
81
|
+
if (t.type === "ADD_TAG" && !(t.allowedTags || []).length) errs.push(`${id}: нужен allowedTags`);
|
|
82
|
+
for (const k of ["amoLeadId", "amoContactId", "amoLeadName", "amoPrice", "amoContactName", "amoPhone", "amoEmail", "httpUrlTemplate", "httpBodyTemplate"]) {
|
|
83
|
+
if (t[k]) placeholders(t[k], known, errs, id);
|
|
84
|
+
}
|
|
85
|
+
for (const c of t.sheetsRowTemplate || []) placeholders(c, known, errs, id);
|
|
86
|
+
}
|
|
87
|
+
return errs;
|
|
88
|
+
}
|
package/src/index.mjs
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import { createInterface } from "node:readline";
|
|
24
|
+
import { validateAgentTools } from "./agent-tools.mjs";
|
|
24
25
|
import { fileURLToPath } from "node:url";
|
|
25
26
|
import { spawn } from "node:child_process";
|
|
26
27
|
import os from "node:os";
|
|
@@ -1098,7 +1099,7 @@ const TOOLS = [
|
|
|
1098
1099
|
{ name: "agent_list", description: "Список ИИ-агентов пользователя (GET /api/bots/agents): карточки — id, имя, статус. Read-only.", inputSchema: { type: "object", properties: {} } },
|
|
1099
1100
|
{ name: "agent_get", description: "ИИ-агент по id (GET /api/bots/agents/{agentId}): настройки (имя, язык, тон, длина/формат ответа, инструкции, запретные/передаточные темы, kbId базы знаний, статус). Чужой агент — 404. Read-only.", inputSchema: { type: "object", properties: { agentId: { type: "string" } }, required: ["agentId"] } },
|
|
1100
1101
|
{ name: "agent_create", description: "Создать ИИ-агента (POST /api/bots/agents): name, description — необязательны. Вместе с агентом создаётся его база знаний (kbId в ответе) — дальше kb_add_qa/kb_add_text/kb_add_site.", inputSchema: { type: "object", properties: { name: { type: "string" }, description: { type: "string" } } } },
|
|
1101
|
-
{ name: "agent_update", description: "Изменить настройки агента (PATCH /api/bots/agents/{agentId}): patch — объект с полями для правки (name, description, language: RU|EN|AUTO, tone: FRIENDLY|NEUTRAL|FORMAL, answerLength: SHORT|MEDIUM|LONG, format: PLAIN|LIST_FRIENDLY, instructions, forbiddenTopics[], handoffTopics[], fallback). Применяется частично — передавай только то, что меняешь.", inputSchema: { type: "object", properties: { agentId: { type: "string" }, patch: { type: "object", description: "Поля агента для частичного обновления" } }, required: ["agentId", "patch"] } },
|
|
1102
|
+
{ name: "agent_update", description: "Изменить настройки агента (PATCH /api/bots/agents/{agentId}): patch — объект с полями для правки (name, description, language: RU|EN|AUTO, tone: FRIENDLY|NEUTRAL|FORMAL, answerLength: SHORT|MEDIUM|LONG, format: PLAIN|LIST_FRIENDLY, instructions, forbiddenTopics[], handoffTopics[], fallback, tools[]). Применяется частично — передавай только то, что меняешь. tools[] заменяет список целиком (до 10). Инструменты ЧТЕНИЯ: AMO_LOOKUP {amoConnectionId}, SHEETS_LOOKUP {sheetsEmail,sheetsSpreadsheetId,sheetsLookupColumn,…}, HTTP_API {httpUrlTemplate,httpParams}. Инструменты ЗАПИСИ (включаются явно по одному, id a-z0-9_): AMO_CREATE_LEAD {amoConnectionId, amoLeadName, amoContactName?, amoPhone?, amoEmail?, amoPrice?, amoPipelineId?, amoStatusId?}, AMO_UPDATE_LEAD {amoLeadId + что обновлять}, AMO_UPDATE_CONTACT {amoContactId, amoContactName?|amoPhone?|amoEmail?}, SHEETS_APPEND {sheetsEmail,sheetsSpreadsheetId,sheetsSheetName?, sheetsRowTemplate:[ячейки]}, HTTP_POST {httpUrlTemplate, httpBodyTemplate? (JSON-шаблон), httpHeaders?:[{name,value}], httpJsonPath?}, SET_FIELD {allowedFields[]}, ADD_TAG {allowedTags[]}, HANDOFF. Для записи (кроме SET_FIELD/ADD_TAG/HANDOFF) обязательна argsSchema — плоская JSON-схема {properties:{имя:{type:string|number|integer|boolean, description?, enum?, pattern?, maxLength?, minLength?, minimum?, maximum?}}, required?:[]}; в шаблонах {имя} подставляет аргумент, {now} — время. Общие поля записи: confirmBeforeRun (по умолчанию true для внешних записей — агент сначала спрашивает согласие пользователя), limitPerConversation (по умолч. 3, макс 20), limitPerDay (по умолч. 100, макс 1000). В публичном веб-чате инструменты записи не работают. Перед отправкой tools[] проверяются локально, ошибки схем возвращаются списком.", inputSchema: { type: "object", properties: { agentId: { type: "string" }, patch: { type: "object", description: "Поля агента для частичного обновления" } }, required: ["agentId", "patch"] } },
|
|
1102
1103
|
{ name: "agent_publish", description: "Опубликовать агента (POST /api/bots/agents/{agentId}/publish): агент начинает отвечать в подключённых сценариях. HTTP 409 CHECKLIST_FAILED с чеклистом, если агент ещё не готов (нет базы знаний, пустые инструкции и т.п.) — агент не меняется.", inputSchema: { type: "object", properties: { agentId: { type: "string" } }, required: ["agentId"] } },
|
|
1103
1104
|
{ name: "agent_health", description: "Здоровье базы знаний агента (GET /api/bots/agents/{agentId}/health): счётчики документов/фрагментов по запросу, без кэша. Read-only.", inputSchema: { type: "object", properties: { agentId: { type: "string" } }, required: ["agentId"] } },
|
|
1104
1105
|
{ name: "agent_test_chat", description: "Проверить ответ агента в песочнице без отправки клиенту (POST /api/bots/agents/{agentId}/test-chat): question (1–1000 символов), history — необязательная история диалога [{role: client|agent, text}], до 12 реплик. ТРАТИТ бюджет ИИ, как настоящий ответ — не вызывай массово.", inputSchema: { type: "object", properties: { agentId: { type: "string" }, question: { type: "string" }, history: { type: "array", items: { type: "object", properties: { role: { type: "string", enum: ["client", "agent"] }, text: { type: "string" } } } } }, required: ["agentId", "question"] } },
|
|
@@ -1236,7 +1237,8 @@ const TOOLS = [
|
|
|
1236
1237
|
{ name: "bot_subscriber_analytics", description: "Новые подписчики/активность бота по дням (GET /api/bots/{botId}/analytics/subscribers?from&to): {newSubscribers, active, total, legacyExcluded, trend:[{day,newSubscribers,active}]}. from/to обязательны — ГГГГ-ММ-ДД, диапазон обрезается сервером до 92 дней. legacyExcluded — сессии без даты создания (заведены до этой фичи): не входят в newSubscribers/trend, но учтены в total. active — по updatedAt в диапазоне (одна сессия может посчитаться в несколько дней). Read-only.", inputSchema: { type: "object", properties: { botId: { type: "string" }, from: { type: "string", description: "ГГГГ-ММ-ДД" }, to: { type: "string", description: "ГГГГ-ММ-ДД" } }, required: ["botId", "from", "to"] } },
|
|
1237
1238
|
{ name: "scenario_timeseries", description: "Дневной ряд прогонов сценария (GET /api/bots/graphs/{graphId}/analytics/timeseries?from&to): [{day, ok, failed, other}] — по одной строке на каждый день диапазона (пустые дни — нулями), other = PARKED/NO_MATCH/PARTIAL (не отброшены). from/to обязательны — ГГГГ-ММ-ДД, диапазон обрезается до 92 дней. Разбивку по узлам сценария (не по дням) даёт graph_analytics — это разные срезы одних данных. Read-only.", inputSchema: { type: "object", properties: { graphId: { type: "string" }, from: { type: "string", description: "ГГГГ-ММ-ДД" }, to: { type: "string", description: "ГГГГ-ММ-ДД" } }, required: ["graphId", "from", "to"] } },
|
|
1238
1239
|
// ---- ИИ-агенты: инструменты (вызов внешних систем моделью) ----
|
|
1239
|
-
{ name: "agent_tool_test", description: "Проверить инструмент агента напрямую, БЕЗ модели (POST /api/bots/agents/{agentId}/tools/{toolId}/test): params — аргументы вызова (AMO_LOOKUP: query ИЛИ leadId; SHEETS_LOOKUP: value; HTTP_API: имена из httpParams этого инструмента). Проверяет только конфиг инструмента (подключение amoCRM/таблицу Sheets/URL) — модель не вызывается, бюджет ИИ не тратится. {ok:true, result} при успехе; ошибка конфига/внешнего сервиса — HTTP 400 {ok:false, error} с причиной на русском (никогда не 500). toolId — id инструмента из agent_get → tools[].id (настраивается через agent_update {patch:{tools:[...]}})." , inputSchema: { type: "object", properties: { agentId: { type: "string" }, toolId: { type: "string" }, params: { type: "object", description: "Аргументы вызова инструмента (см. описание)" } }, required: ["agentId", "toolId"] } },
|
|
1240
|
+
{ name: "agent_tool_test", description: "Проверить инструмент агента напрямую, БЕЗ модели (POST /api/bots/agents/{agentId}/tools/{toolId}/test): params — аргументы вызова (AMO_LOOKUP: query ИЛИ leadId; SHEETS_LOOKUP: value; HTTP_API: имена из httpParams этого инструмента). Проверяет только конфиг инструмента (подключение amoCRM/таблицу Sheets/URL) — модель не вызывается, бюджет ИИ не тратится. {ok:true, result} при успехе; ошибка конфига/внешнего сервиса — HTTP 400 {ok:false, error} с причиной на русском (никогда не 500). toolId — id инструмента из agent_get → tools[].id (настраивается через agent_update {patch:{tools:[...]}}). ИНСТРУМЕНТЫ ЗАПИСИ (AMO_CREATE_LEAD/…/HTTP_POST/SET_FIELD/ADD_TAG/HANDOFF): params — аргументы по argsSchema инструмента (для SET_FIELD: field,value; ADD_TAG: tag; HANDOFF: reason); по умолчанию это ПЕСОЧНИЦА (dryRun:true) — аргументы проверяются по схеме, возвращается что было бы отправлено, во внешние системы ничего не уходит; live:true отправляет по-настоящему (только внешние записи: amoCRM, Google Таблица, POST) — используй осознанно, это реальная запись." , inputSchema: { type: "object", properties: { agentId: { type: "string" }, toolId: { type: "string" }, params: { type: "object", description: "Аргументы вызова инструмента (см. описание)" }, live: { type: "boolean", description: "Только для инструментов записи: true — выполнить по-настоящему, иначе песочница (dry-run)" } }, required: ["agentId", "toolId"] } },
|
|
1241
|
+
{ name: "agent_tool_calls", description: "Журнал вызовов инструментов ЗАПИСИ агента (GET /api/bots/agents/{agentId}/tool-calls): когда, какой инструмент, кто инициатор (AGENT/OWNER_TEST), статус (OK, FAILED, PENDING_CONFIRM, REJECTED по лимиту, DRY_RUN песочницы), аргументы без секретов, результат/ошибка. Фильтр toolId, limit до 200 (по умолч. 50). Хранится 90 дней. Read-only.", inputSchema: { type: "object", properties: { agentId: { type: "string" }, toolId: { type: "string" }, limit: { type: "number" } }, required: ["agentId"] } },
|
|
1240
1242
|
// ---- Заявки сайта: файлы вложений ----
|
|
1241
1243
|
{ 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"] } },
|
|
1242
1244
|
// ---- Вебхук-сценарии (/api/scenarios, PAT-доступ открыт решением владельца 14.10) ----
|
|
@@ -1552,6 +1554,10 @@ async function handleCall(params) {
|
|
|
1552
1554
|
case "agent_create": return okResult(await api("/api/bots/agents", { method: "POST", body: { name: a.name, description: a.description } }));
|
|
1553
1555
|
case "agent_update": {
|
|
1554
1556
|
if (!a.patch || typeof a.patch !== "object") throw new Error("Передай patch — объект с полями агента для правки.");
|
|
1557
|
+
if (a.patch.tools !== undefined) {
|
|
1558
|
+
const errs = validateAgentTools(a.patch.tools);
|
|
1559
|
+
if (errs.length) throw new Error("Инструменты агента не прошли проверку:\n- " + errs.join("\n- "));
|
|
1560
|
+
}
|
|
1555
1561
|
return okResult(await api(`/api/bots/agents/${a.agentId}`, { method: "PATCH", body: a.patch }));
|
|
1556
1562
|
}
|
|
1557
1563
|
case "agent_publish": return okResult(await api(`/api/bots/agents/${a.agentId}/publish`, { method: "POST" }));
|
|
@@ -2445,7 +2451,16 @@ async function handleCall(params) {
|
|
|
2445
2451
|
case "agent_tool_test": {
|
|
2446
2452
|
if (!a.agentId) throw new Error("Передай agentId.");
|
|
2447
2453
|
const toolId = idArg(a.toolId, "toolId");
|
|
2448
|
-
|
|
2454
|
+
const body = { params: a.params || {} };
|
|
2455
|
+
if (a.live === true) body.live = true;
|
|
2456
|
+
return okResult(await api(`/api/bots/agents/${a.agentId}/tools/${toolId}/test`, { method: "POST", body }));
|
|
2457
|
+
}
|
|
2458
|
+
case "agent_tool_calls": {
|
|
2459
|
+
if (!a.agentId) throw new Error("Передай agentId.");
|
|
2460
|
+
const q = new URLSearchParams();
|
|
2461
|
+
if (a.toolId) q.set("toolId", idArg(a.toolId, "toolId"));
|
|
2462
|
+
if (a.limit != null) q.set("limit", String(Math.max(1, Math.min(200, Number(a.limit) || 50))));
|
|
2463
|
+
return okResult(await api(`/api/bots/agents/${a.agentId}/tool-calls${q.toString() ? "?" + q : ""}`));
|
|
2449
2464
|
}
|
|
2450
2465
|
// ---- Заявки сайта: файлы ----
|
|
2451
2466
|
case "site_lead_file_download": {
|