@besales/mcp 0.46.0 → 0.47.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.
Files changed (182) hide show
  1. package/README.md +96 -55
  2. package/dist/auth/connection-store.d.ts +21 -1
  3. package/dist/auth/connection-store.js +46 -2
  4. package/dist/auth/connection-store.js.map +1 -1
  5. package/dist/auth/oauth-client.d.ts +5 -1
  6. package/dist/auth/oauth-client.js +22 -12
  7. package/dist/auth/oauth-client.js.map +1 -1
  8. package/dist/cli.js +14 -5
  9. package/dist/cli.js.map +1 -1
  10. package/dist/http/api-client.d.ts +16 -1
  11. package/dist/http/api-client.js +50 -2
  12. package/dist/http/api-client.js.map +1 -1
  13. package/dist/instructions/server-instructions.d.ts +4 -13
  14. package/dist/instructions/server-instructions.js +68 -274
  15. package/dist/instructions/server-instructions.js.map +1 -1
  16. package/dist/prompts/registry.d.ts +15 -0
  17. package/dist/prompts/registry.js +83 -0
  18. package/dist/prompts/registry.js.map +1 -0
  19. package/dist/resources/concepts/feedback-sheets.md +7 -4
  20. package/dist/resources/concepts/handoff.md +4 -1
  21. package/dist/resources/concepts/sandbox.md +14 -5
  22. package/dist/resources/concepts/triggers.md +6 -2
  23. package/dist/resources/concepts/workbook-classification.md +26 -21
  24. package/dist/resources/docs/campaigns.md +20 -0
  25. package/dist/resources/docs/follow-up.md +19 -0
  26. package/dist/resources/docs/platforms.md +2 -2
  27. package/dist/resources/docs/routing.md +18 -0
  28. package/dist/resources/docs/task-state.md +30 -0
  29. package/dist/resources/docs/trigger-actions.md +28 -0
  30. package/dist/resources/docs/variables.md +18 -0
  31. package/dist/resources/registry.js +51 -2
  32. package/dist/resources/registry.js.map +1 -1
  33. package/dist/resources/workflows/extract-from-document.md +8 -1
  34. package/dist/resources/workflows/iterate-with-sandbox.md +5 -2
  35. package/dist/resources/workflows/platform-setup-from-scratch.md +90 -2
  36. package/dist/resources/workflows/production-readiness-check.md +22 -18
  37. package/dist/schemas/mcp-tools.json +1058 -99
  38. package/dist/server.d.ts +2 -0
  39. package/dist/server.js +8 -2
  40. package/dist/server.js.map +1 -1
  41. package/dist/tasks/task-state-store.d.ts +68 -0
  42. package/dist/tasks/task-state-store.js +278 -0
  43. package/dist/tasks/task-state-store.js.map +1 -0
  44. package/dist/tools/annotations.d.ts +7 -0
  45. package/dist/tools/annotations.js +16 -0
  46. package/dist/tools/annotations.js.map +1 -0
  47. package/dist/tools/contracts.js +7 -1
  48. package/dist/tools/contracts.js.map +1 -1
  49. package/dist/tools/definitions/agent-design.d.ts +41 -20
  50. package/dist/tools/definitions/agent-design.js +189 -19
  51. package/dist/tools/definitions/agent-design.js.map +1 -1
  52. package/dist/tools/definitions/analytics.d.ts +40 -0
  53. package/dist/tools/definitions/analytics.js +233 -0
  54. package/dist/tools/definitions/analytics.js.map +1 -0
  55. package/dist/tools/definitions/broadcast-analytics.js +12 -0
  56. package/dist/tools/definitions/broadcast-analytics.js.map +1 -1
  57. package/dist/tools/definitions/broadcast-mailing-variants.d.ts +2 -0
  58. package/dist/tools/definitions/broadcast-mailing-variants.js +10 -0
  59. package/dist/tools/definitions/broadcast-mailing-variants.js.map +1 -1
  60. package/dist/tools/definitions/broadcast-scheduler.d.ts +7 -1
  61. package/dist/tools/definitions/broadcast-scheduler.js +71 -4
  62. package/dist/tools/definitions/broadcast-scheduler.js.map +1 -1
  63. package/dist/tools/definitions/contacts.d.ts +15 -0
  64. package/dist/tools/definitions/contacts.js +84 -0
  65. package/dist/tools/definitions/contacts.js.map +1 -0
  66. package/dist/tools/definitions/crm-platform.d.ts +2 -0
  67. package/dist/tools/definitions/crm-platform.js +75 -5
  68. package/dist/tools/definitions/crm-platform.js.map +1 -1
  69. package/dist/tools/definitions/datasource.d.ts +1 -0
  70. package/dist/tools/definitions/datasource.js +16 -1
  71. package/dist/tools/definitions/datasource.js.map +1 -1
  72. package/dist/tools/definitions/dialogue-audit.d.ts +2 -2
  73. package/dist/tools/definitions/dialogue-audit.js +54 -16
  74. package/dist/tools/definitions/dialogue-audit.js.map +1 -1
  75. package/dist/tools/definitions/dialogue-export.d.ts +1 -1
  76. package/dist/tools/definitions/dialogue-export.js +12 -0
  77. package/dist/tools/definitions/dialogue-export.js.map +1 -1
  78. package/dist/tools/definitions/external-api-channels.js +39 -12
  79. package/dist/tools/definitions/external-api-channels.js.map +1 -1
  80. package/dist/tools/definitions/follow-up-funnel-test.js +4 -0
  81. package/dist/tools/definitions/follow-up-funnel-test.js.map +1 -1
  82. package/dist/tools/definitions/follow-up-setup.js +3 -0
  83. package/dist/tools/definitions/follow-up-setup.js.map +1 -1
  84. package/dist/tools/definitions/follow-up.d.ts +1 -1
  85. package/dist/tools/definitions/follow-up.js +4 -0
  86. package/dist/tools/definitions/follow-up.js.map +1 -1
  87. package/dist/tools/definitions/getcourse-anchor-refresh.d.ts +3 -0
  88. package/dist/tools/definitions/getcourse-anchor-refresh.js +29 -2
  89. package/dist/tools/definitions/getcourse-anchor-refresh.js.map +1 -1
  90. package/dist/tools/definitions/getcourse-cold-welcome.d.ts +12 -2
  91. package/dist/tools/definitions/getcourse-cold-welcome.js +79 -4
  92. package/dist/tools/definitions/getcourse-cold-welcome.js.map +1 -1
  93. package/dist/tools/definitions/getcourse-wave.js +14 -0
  94. package/dist/tools/definitions/getcourse-wave.js.map +1 -1
  95. package/dist/tools/definitions/help.d.ts +8 -0
  96. package/dist/tools/definitions/help.js +219 -0
  97. package/dist/tools/definitions/help.js.map +1 -0
  98. package/dist/tools/definitions/knowledge.d.ts +27 -5
  99. package/dist/tools/definitions/knowledge.js +200 -33
  100. package/dist/tools/definitions/knowledge.js.map +1 -1
  101. package/dist/tools/definitions/link-tracking.js +9 -0
  102. package/dist/tools/definitions/link-tracking.js.map +1 -1
  103. package/dist/tools/definitions/model-comparison.js +6 -0
  104. package/dist/tools/definitions/model-comparison.js.map +1 -1
  105. package/dist/tools/definitions/operational-overview.d.ts +3 -0
  106. package/dist/tools/definitions/operational-overview.js +23 -0
  107. package/dist/tools/definitions/operational-overview.js.map +1 -0
  108. package/dist/tools/definitions/operator-workflow.d.ts +20 -0
  109. package/dist/tools/definitions/operator-workflow.js +114 -0
  110. package/dist/tools/definitions/operator-workflow.js.map +1 -0
  111. package/dist/tools/definitions/platform-setup.d.ts +25 -3
  112. package/dist/tools/definitions/platform-setup.js +198 -5
  113. package/dist/tools/definitions/platform-setup.js.map +1 -1
  114. package/dist/tools/definitions/scheduled-tasks.js +2 -0
  115. package/dist/tools/definitions/scheduled-tasks.js.map +1 -1
  116. package/dist/tools/definitions/session.js +103 -5
  117. package/dist/tools/definitions/session.js.map +1 -1
  118. package/dist/tools/definitions/shared.d.ts +153 -11
  119. package/dist/tools/definitions/shared.js +59 -9
  120. package/dist/tools/definitions/shared.js.map +1 -1
  121. package/dist/tools/definitions/task-state.d.ts +76 -0
  122. package/dist/tools/definitions/task-state.js +301 -0
  123. package/dist/tools/definitions/task-state.js.map +1 -0
  124. package/dist/tools/definitions/trigger-contact-claims.js +2 -0
  125. package/dist/tools/definitions/trigger-contact-claims.js.map +1 -1
  126. package/dist/tools/definitions.d.ts +265 -31
  127. package/dist/tools/definitions.js +12 -0
  128. package/dist/tools/definitions.js.map +1 -1
  129. package/dist/tools/description-localization.d.ts +2 -0
  130. package/dist/tools/description-localization.js +46 -0
  131. package/dist/tools/description-localization.js.map +1 -0
  132. package/dist/tools/incomplete-list.d.ts +23 -0
  133. package/dist/tools/incomplete-list.js +39 -0
  134. package/dist/tools/incomplete-list.js.map +1 -0
  135. package/dist/tools/inventory.d.ts +10 -0
  136. package/dist/tools/inventory.js +49 -0
  137. package/dist/tools/inventory.js.map +1 -0
  138. package/dist/tools/registry-mapping-cases-core.js +111 -2
  139. package/dist/tools/registry-mapping-cases-core.js.map +1 -1
  140. package/dist/tools/registry-mapping-cases-platform.js +69 -0
  141. package/dist/tools/registry-mapping-cases-platform.js.map +1 -1
  142. package/dist/tools/registry.d.ts +2 -0
  143. package/dist/tools/registry.js +60 -8
  144. package/dist/tools/registry.js.map +1 -1
  145. package/dist/tools/result-envelope.d.ts +46 -0
  146. package/dist/tools/result-envelope.js +184 -0
  147. package/dist/tools/result-envelope.js.map +1 -0
  148. package/dist/tools/result.d.ts +41 -3
  149. package/dist/tools/result.js +229 -27
  150. package/dist/tools/result.js.map +1 -1
  151. package/dist/tools/tool-title.d.ts +2 -0
  152. package/dist/tools/tool-title.js +12 -0
  153. package/dist/tools/tool-title.js.map +1 -0
  154. package/dist/tools/toolsets.d.ts +37 -0
  155. package/dist/tools/toolsets.js +64 -0
  156. package/dist/tools/toolsets.js.map +1 -0
  157. package/dist/tools/verify-with.d.ts +5 -0
  158. package/dist/tools/verify-with.js +36 -0
  159. package/dist/tools/verify-with.js.map +1 -0
  160. package/dist/types/api-contract.gen.d.ts +1421 -80
  161. package/dist/utils/logger.d.ts +11 -0
  162. package/dist/utils/logger.js +64 -8
  163. package/dist/utils/logger.js.map +1 -1
  164. package/docs/agent-usage-guide.full.ru.md +20 -15
  165. package/docs/agent-usage-guide.ru.md +24 -16
  166. package/docs/async-operation-lifecycle.md +46 -0
  167. package/docs/connect.ru.md +120 -0
  168. package/docs/evals/model-behavior-scorecard.md +80 -0
  169. package/docs/failure-semantics.md +56 -0
  170. package/docs/help-navigation.md +48 -0
  171. package/docs/host-compatibility-matrix.md +153 -0
  172. package/docs/host-setup.md +22 -6
  173. package/docs/operation-coverage.md +114 -0
  174. package/docs/pii-policy.md +46 -0
  175. package/docs/response-size-budget.md +150 -0
  176. package/docs/task-state.md +58 -0
  177. package/docs/tool-effects.md +46 -0
  178. package/docs/tool-surface.md +91 -0
  179. package/package.json +6 -2
  180. package/scripts/model-eval-server.js +213 -0
  181. package/scripts/probe-mcp-protocol.js +134 -0
  182. package/scripts/run-model-eval.js +447 -0
@@ -0,0 +1,48 @@
1
+ # Навигация по справке Besales MCP
2
+
3
+ ## Зачем нужен tool-мост
4
+
5
+ Некоторые MCP-хосты показывают модели tools, но не передают список resources или не предлагают
6
+ прочитать resource автоматически. Поэтому справка доступна двумя равноправными путями:
7
+
8
+ - MCP resource по URI `besales://...`;
9
+ - `besales_help_search` и `besales_help_get`, которые читают те же файлы пакета.
10
+
11
+ В ответах tool-моста есть `version`, `source` и `complete`. Это позволяет отличить актуальную
12
+ справку пакета от пересказа модели и понять, был ли получен весь документ или один раздел.
13
+
14
+ ## Поиск
15
+
16
+ `besales_help_search({query})` принимает обычную русскую или английскую формулировку. Встроенные
17
+ синонимы связывают лексику пользователя и продукта: «дожим» с follow-up, «волна» с broadcast,
18
+ «бот молчит» с silence/handoff. Ответ разделяет найденные операции (`operations`) и документы
19
+ (`results`). Для операции также возвращаются `score` и `matchedBy`: совпадение по имени или по
20
+ пользовательскому намерению.
21
+
22
+ Все активные инструменты получают короткую русскую формулировку намерения в описании, которое
23
+ публикуется через `tools/list`. Английская часть сохраняется для существующих клиентов и поиска.
24
+ Так модель может найти создание агента, изменение маршрутизации, запуск GetCourse-волны или
25
+ финализацию промпта по обычному русскому запросу без знания технического имени инструмента.
26
+
27
+ Поиск не вызывает API и не читает данные workspace. Если результатов нет, нужно уточнить объект
28
+ или цель, а не угадывать имя инструмента.
29
+
30
+ ## Точечное и полное чтение
31
+
32
+ `besales_help_get({uri})` возвращает индекс заголовков. Затем можно запросить один раздел через
33
+ `section` или весь документ через `full=true`. Неоднозначный фрагмент заголовка возвращает список
34
+ кандидатов; отсутствующий раздел возвращает `found=false`.
35
+
36
+ Полная версия остаётся доступной, но для небольшой операции лучше читать один релевантный раздел.
37
+ Содержимое resource и tool-ответа берётся из одного файла, поэтому две копии документа не
38
+ расходятся.
39
+
40
+ ## Проверка
41
+
42
+ - `src/tools/definitions/help.spec.ts` проверяет паритет русских и английских намерений, индекс,
43
+ раздел и полное чтение.
44
+ - `src/tools/description-localization.spec.ts` не допускает активный инструмент без русской
45
+ формулировки в опубликованном описании.
46
+ - `src/resources/registry.spec.ts` проверяет ссылки на реальные инструменты во всех resources и
47
+ коротких server instructions.
48
+ - `src/server.spec.ts` проверяет публикацию server instructions через MCP initialize.
@@ -0,0 +1,153 @@
1
+ # Матрица хостов, версий и установки
2
+
3
+ Снимок: 2026-09-11. Это проверяемая граница текущего hardening, а не обещание для любого
4
+ MCP-клиента.
5
+
6
+ ## Линии продукта
7
+
8
+ | Линия | MCP | API | Источник API-контракта | Tool schema | Transport |
9
+ |---|---|---|---|---|---|
10
+ | main | `be-sales/mcp`, `codex/mcp-hardening-main-20260910`, `0.46.0` | `be-sales/back-main`, `codex/mcp-hardening-api-main-20260910`, `1.0.1` | loose `animaly/docs/specs/v2/contracts/openapi-besales-api.yaml` → `src/types/api-contract.gen.ts` | tracked `besales-mcp/src/schemas/mcp-tools.json`; build копирует его в dist | local stdio |
11
+ | v2 beta | тот же GitHub repo, отдельная ветка `codex/mcp-hardening-v2-20260910`, `1.0.0-beta.1` | `be-sales/besales-api`, `codex/mcp-hardening-api-v2-20260910`, `0.0.1` | `besales-api/contracts/openapi-besales-api.yaml` → generated types | локальный `src/schemas/mcp-tools.json` + route export | local stdio |
12
+
13
+ Main и v2 не взаимозаменяемы по совпадению имени файла. Изменение переносится только после
14
+ сверки route, DTO, scope, ответа и теста своей пары.
15
+
16
+ Общий loose `docs/specs/v2/contracts/mcp-tools.json` является целью v2 registry publish и больше
17
+ не копируется внутрь main. Это устраняет перезапись канона одной линии контрактом другой; main
18
+ проверяет собственный tracked schema, v2 публикует свой schema в явно выбранные targets.
19
+
20
+ ## Поддерживаемые хосты этой итерации
21
+
22
+ | Хост | Проверенная версия | Фактическое поведение | Статус |
23
+ |---|---|---|---|
24
+ | MCP SDK / Inspector | SDK из lockfile, raw stdio | полный initialize; main: 253 tools / 38 resources / 5 prompts, v2: 213 / 38 / 5; schema validation до HTTP | блокирующий protocol gate |
25
+ | Codex CLI / App | `codex-cli 0.153.4` | один сервер `besales`; tools могут грузиться отложенно; resources модели не гарантированы, поэтому есть `besales_help_*` | поддержан |
26
+ | Claude Code | `2.1.220` | stdio подключён; видит tools/resources/prompts; server instructions режет **4123 → 2048 символов** | поддержан с полезным префиксом и help-tools |
27
+ | Claude Desktop | app `1.49585.0` | local stdio; два намеренно разных подключения `besales-tg` и `besales-ksamata`, каждое прибито к своему workspace | поддержан; ручной UI smoke после установки |
28
+
29
+ Новый неизвестный хост не становится блокирующим автоматически. Для него сначала повторяются
30
+ raw initialize/list/read/call и модельная оценка.
31
+
32
+ ## Протокольное доказательство main
33
+
34
+ Артефакт: `docs/evidence/2026-09-11-main-wire-protocol.json`.
35
+
36
+ - initialize: `@besales/mcp 0.46.0`, instructions 4123 символа / 6532 UTF-8 байта;
37
+ - capabilities: tools, resources, prompts;
38
+ - tools/list: 253 уникальных имени, 418380 байт;
39
+ - resources/list: 38; каждый URI реально прочитан и снабжён SHA-256;
40
+ - prompts/list: 5;
41
+ - пустой `besales_agent_create` отклонён MCP кодом `-32602` до HTTP: отсутствуют
42
+ `platform_id`, `name`, а `model_lane` не входит в `CHEAP|STANDARD|DEEP`;
43
+ - wire schema сохраняет required, UUID format, enums, вложенные объекты и
44
+ `const: true` + `type: boolean`.
45
+
46
+ Скрипт повторения: `yarn build && yarn host:probe bin/besales-mcp.js <output.json>`.
47
+
48
+ ## Протокольное доказательство v2 beta
49
+
50
+ Артефакт в v2 checkout: `docs/evidence/2026-09-11-v2-wire-protocol.json`.
51
+
52
+ - initialize: `@besales/mcp 1.0.0-beta.1`, instructions 4122 символа / 6538 UTF-8 байт;
53
+ - tools/list: 213 уникальных имён, 322105 байт; resources/list: 38; prompts/list: 5;
54
+ - все resources прочитаны, representative schemas сохранены, пустой `besales_agent_create`
55
+ отклонён кодом `-32602` до API теми же обязательными полями;
56
+ - снимок собран из v2 MCP `912a70a`; API-зависимые main tools в него не копировались.
57
+
58
+ ## Что видит модель
59
+
60
+ У установленной до hardening main-версии Codex App было 215 активных tools. Старый сервер
61
+ добавлял полный текст инструкций в описание каждого tool: 22385 символов у одного описания,
62
+ 4747836 символов суммарно. Это подтверждённый дефект версии, а не лимит MCP.
63
+
64
+ Новая версия передаёт инструкции один раз через initialize. В первых 2048 символах, которые
65
+ оставляет проверенный Claude Code, находятся:
66
+
67
+ 1. `besales_workspace_current` и выбор компании по названию;
68
+ 2. overview → точечный platform context;
69
+ 3. `besales_help_search` → `besales_help_get`;
70
+ 4. запрет одинакового повторного read и конкретный следующий шаг;
71
+ 5. запрет секретов в чате и начало правила подтверждений.
72
+
73
+ Глубокие правила после этой границы остаются доступны через тот же registry ресурсов и
74
+ `besales_help_search` / `besales_help_get`; модель не зависит от UI resources конкретного хоста.
75
+
76
+ Повторная проверка model-eval 2026-09-11 отозвала прежний сравнительный PASS:
77
+ все 15 baseline-прогонов были заблокированы политикой approvals хоста до выполнения fixture.
78
+ Сопоставимого baseline нет; улучшение времени, токенов или успешности не доказано.
79
+ Current отдельно дал 14/15 успешных маршрутов и чтение справки в 12/15 на пяти fixture-сценариях
80
+ (`gpt-5.6-luna`, reasoning `low`, три повтора). Это не проверка production API или всех workflows.
81
+ Scorer пересчитывает метрики из исходных calls/events и учитывает только успешные ответы;
82
+ approval-blocked read не считается мутацией. UPD-048 открыт до сопоставимой серии.
83
+ Raw JSONL сохранён в `docs/evidence/model-eval-2026-09-11/`.
84
+
85
+ Claude Code debug подтвердил stdio connection и capabilities tools/resources/prompts, а также
86
+ точный лимит instructions 2048. Модельный ответ не получен: аккаунт вернул 429 weekly limit до
87
+ первого token. Это ограничение хоста/аккаунта, не ошибка MCP; raw protocol и command discovery
88
+ остаются валидны. В Claude Code prompts появляются как
89
+ `/mcp__besales__besales_setup_agent` и аналогичные команды. Обычный текстовый запрос остаётся
90
+ основным и полностью эквивалентным входом.
91
+
92
+ ## Подключения и дубли
93
+
94
+ Наблюдавшиеся «3 сервера / 642 записи» не являются тремя случайными копиями в одном каталоге:
95
+
96
+ - Codex имеет один `besales`;
97
+ - Claude Code имеет один `besales`;
98
+ - Claude Desktop имеет два purpose-specific сервера для разных workspace.
99
+
100
+ Удалять их автоматически нельзя: это уничтожило бы выбор компании. Случайным дублем считается
101
+ только повтор одного executable, API URL и workspace pin внутри одного host. Миграция:
102
+
103
+ 1. оставить по одному имени на уникальную тройку executable/API/workspace;
104
+ 2. обновить executable на проверенный пакет;
105
+ 3. перезапустить host и вызвать `besales_workspace_current`;
106
+ 4. удалить старую запись вручную лишь после успешного smoke новой.
107
+
108
+ Профиль `BESALES_MCP_TOOLSETS` уменьшает поверхность одного сервера и одинаково ограничивает
109
+ list и call; help возвращает скрытые совпадения отдельно, не выдавая их за вызываемые. Профиль не
110
+ заменяет workspace pin и не объединяет разные компании.
111
+
112
+ ## Scopes
113
+
114
+ | Линия | Запрашивает MCP | Поддерживает API | Фактически выдаёт |
115
+ |---|---|---|---|
116
+ | main | `mcp:* mcp:platform-setup mcp:funnel-test read:campaigns launch:campaigns read:contacts write:operators` | тот же ratchet-набор | consent-набор с учётом прав пользователя; exchange возвращает `scopes` и `dropped_scopes` |
117
+ | v2 beta | `mcp:* mcp:platform-setup read:contacts` | та же тройка; `read:platform-setup` входит в composite read | consent выдаёт только поддержанный v2-набор |
118
+
119
+ `besales_workspace_current.effective_scopes` берётся из конкретного выбранного credential.
120
+ Union других ключей workspace не используется. 403 сохраняет названный API недостающий scope;
121
+ reconnect добавляет права и не должен сужать прежние. Main и beta не копируют scope-set друг
122
+ друга.
123
+
124
+ ## Установка, обновление, отзыв
125
+
126
+ Поддержанный пользовательский путь сейчас — local stdio и browser OAuth:
127
+
128
+ 1. host запускает `npx -y @besales/mcp@<проверенная-версия>` либо pinned локальный executable;
129
+ 2. `besales-mcp connect` открывает browser consent; секрет в чат не копируется;
130
+ 3. `besales-mcp status` и `besales_workspace_current` показывают подключение;
131
+ 4. обновление: поменять версию/executable, перезапустить host, повторить raw smoke;
132
+ 5. reconnect нужен для протухшего credential или новых scopes, а не для неоднозначного выбора;
133
+ 6. отзыв выполняется в настройках подключения, затем проверяется 401 именно этого workspace.
134
+
135
+ Keytar остаётся частью local distribution и хранит ключи в системном credential store. Remote
136
+ HTTP/OAuth не вводится в этом инкременте: для него нет ратифицированной модели размещения,
137
+ tenant routing и миграции локальных ключей. Возвращаться к нему следует, когда появится
138
+ server-side host и отдельный threat model. Перенос аккаунта выполняется новым browser connect;
139
+ credential store не копируется между машинами.
140
+
141
+ ## Выпуск
142
+
143
+ - Аддитивные tools/resources/prompts совместимы в minor main и beta build.
144
+ - Новые обязательные поля, default limits и форма результата требуют отдельного rollout.
145
+ - `BESALES_MCP_RESULT_ENVELOPE=on` включает одинаковые text/structuredContent и outputSchema;
146
+ default остаётся off один совместимый релиз.
147
+ - main и `1.0.0-beta.1` не публикуются на один dist-tag.
148
+ - Публикация и деплой в этот документ не входят. После публикации обязателен smoke реально
149
+ установленного пакета после полного restart host; локальный worktree smoke его не заменяет.
150
+
151
+ Официальное отображение prompts в Claude Code: MCP prompt становится командой
152
+ `/mcp__<server>__<prompt>`; resources доступны через `@server:uri`. Источник:
153
+ https://code.claude.com/docs/en/mcp
@@ -80,9 +80,18 @@ node bin/besales-mcp.js connect # workspace B (re-login in the browser
80
80
  node bin/besales-mcp.js connections # list all connections, marks the active one
81
81
  ```
82
82
 
83
+ The connection index includes the workspace name and a masked account label.
84
+ `besales_workspace_current` refreshes missing or day-old labels through the
85
+ lightweight `/auth/mcp-context` endpoint and reports `current`, `cached`, `stale`,
86
+ or `unknown` with `label_updated_at` and a safe refresh reason. This migrates
87
+ legacy entries without six full overview calls. Reconnecting also refreshes
88
+ credentials and labels, but does not choose between several unpinned workspaces.
89
+
83
90
  Workspace-level tools default to the session's workspace; users should not be
84
- asked to provide `workspace_id`. Pin a session with `BESALES_WORKSPACE_ID` in the
85
- host's `env` block, or set the global default with `besales-mcp use <workspaceId>`.
91
+ asked to find or type `workspace_id`. Show the names returned by
92
+ `besales_workspace_current`, then pass the selected UUID to `besales_workspace_use`.
93
+ Pin a session with `BESALES_WORKSPACE_ID` in the host's `env` block, or set the
94
+ global default with `besales-mcp use <workspaceId>`.
86
95
  See the package README "Multiple Workspaces" section for the full precedence.
87
96
 
88
97
  For MCP protocol smoke without live auth, use the local mock API:
@@ -120,12 +129,17 @@ BESALES_API_BASE_URL=http://localhost:3000/api/v2 \
120
129
  BESALES_API_BASE_URL=http://localhost:3000/api/v2 \
121
130
  yarn dlx @modelcontextprotocol/inspector --cli --method resources/list \
122
131
  node bin/besales-mcp.js
132
+
133
+ BESALES_API_BASE_URL=http://localhost:3000/api/v2 \
134
+ yarn dlx @modelcontextprotocol/inspector --cli --method prompts/list \
135
+ node bin/besales-mcp.js
123
136
  ```
124
137
 
125
138
  Expected:
126
139
 
127
- - 145 tools are listed.
128
- - 31 resources are listed.
140
+ - 248 tools are listed.
141
+ - 38 resources are listed.
142
+ - 5 optional task prompts are listed.
129
143
  - `besales://concepts/external-execution` opens.
130
144
  - A direct tool, for example `besales_icp_create`, reaches `ai-aniomaly`.
131
145
  - An external pair returns instructions and accepts submit.
@@ -230,6 +244,8 @@ are present, then run:
230
244
 
231
245
  1. One direct tool (`besales_icp_create`).
232
246
  2. One external pair (`*_get_instructions` then matching `*_submit`).
247
+ 3. A natural-language help request through `besales_help_search`, followed by one section through
248
+ `besales_help_get`. This check is required when the host does not expose resources to the model.
233
249
 
234
250
  For the ICP analysis pair, `besales_icp_analysis_submit` personas must include
235
251
  `segment_id`. Use either an existing ICP segment id or the `id` of a segment in
@@ -243,7 +259,7 @@ session may need an app/session restart before new tools appear.
243
259
  Codex App can defer MCP tool schemas until the model searches for them. This is
244
260
  host behavior, not a `besales-mcp` server problem: `codex mcp list/get besales`
245
261
  and MCP Inspector `tools/list` are still the source checks for whether the
246
- server is installed and exposing all 145 tools.
262
+ server is installed and exposing all 248 tools.
247
263
 
248
264
  User-facing prompts must stay natural. Users should ask for the business action,
249
265
  for example:
@@ -264,7 +280,7 @@ Use this matrix for local validation before staging/prod checks.
264
280
 
265
281
  | Host | Config check | Protocol check | Direct tool | External pair |
266
282
  |---|---|---|---|---|
267
- | MCP Inspector | command launches | 145 tools / 31 resources | `besales_icp_create` | instructions -> submit |
283
+ | MCP Inspector | command launches | 248 tools / 38 resources / 5 prompts | `besales_icp_create` | instructions -> submit |
268
284
  | Claude Desktop | config JSON has `besales` | server connected | `besales_icp_create` | instructions -> submit |
269
285
  | Claude Code | `claude mcp list` has `besales` | `/mcp` connected | `besales_icp_create` | instructions -> submit |
270
286
  | Codex CLI | `codex mcp list` has `besales` | new session sees tools | `besales_icp_create` | instructions -> submit |
@@ -0,0 +1,114 @@
1
+ # Покрытие пользовательских операций
2
+
3
+ Снимок main: 2026-09-11. Единица — пользовательская операция, а не страница UI. Статусы:
4
+ **full** — есть MCP read/write и проверка; **partial** — часть пути требует UI или нет полного
5
+ lifecycle; **missing API** — безопасного `/api/v2` контракта нет; **intentional** — действие
6
+ сознательно не публикуется.
7
+
8
+ ## Матрица
9
+
10
+ | Область / операция | MCP | API `/api/v2` | Scope / эффект | Проверка | Статус и решение |
11
+ |---|---|---|---|---|---|
12
+ | workspace / platform / agents | overview, context sections, agent search/get/create/update | есть | setup scopes; read/write | повторный get/context | full |
13
+ | prompt / ICP / sandbox | direct + 7 external pairs + lifecycle | есть | read/write/spend | lint, version, run status/findings | full; draft-bound sandbox см. отдельное решение |
14
+ | knowledge / workbook | CRUD, page read, classification, archive, reindex status | есть | read/write | fingerprint, get/status | full |
15
+ | CRM / pipelines / fields | setup, list/get/upsert, mapping, operators CRM | есть | setup scopes; read/write | health/get/list | full; CRM operator не workspace operator |
16
+ | data sources / 1С | setup/status/list/health/catalog/roles/vectorize | есть | setup scopes; health пишет status | status/health/catalog | partial: catalogs API без cursor |
17
+ | follow-up | sequences, step stats, reactions, funnel test | есть | read/write/spend | list/stats/run status | full для текущего API |
18
+ | outgoing waves / scheduler | preview/import/status, schedules, cohorts, analytics | есть | campaign scopes; spend | status/analytics | partial: несколько legacy lists без cursor |
19
+ | incoming analytics | summary/topics/speed/money/cycle/quality | есть, делегирует UI service | `read:analytics`, read | те же DTO и canonical totals | full для агрегатов; person drill-down отдельно |
20
+ | funnel analytics | meta/summary/cohorts | есть, делегирует UI service | `read:analytics`, read | тот же view/period/grain, что UI | full для основных агрегатов |
21
+ | custom reports | list/get/run | есть | `read:analytics`, read | report definition + grandTotal | full для чтения; person drill-down отдельно |
22
+ | AI ledger / costs | summary/tasks/breakdown | есть, делегирует `AiUsageService` | `read:analytics`, read | ledger totals + unpricedEvents | full для чтения; прайс модели не замена ledger |
23
+ | operational overview | цепочки, расписания, jobs, action errors, условная operator queue | есть | `read:analytics`; queue ещё `read:conversations` | due/heartbeat + drill-down | full для сводки; детальные chats/operators ниже |
24
+ | contacts | search/detail/diagnostics + dialogue transcript | list/detail/messages/state/history есть | contacts/conversations, read | detail/history/diagnostics | full для read-only работы |
25
+ | chats / operator queue | dialogue query/transcript + paginated queue | есть | `read:conversations` | status + PlatformWorkLog evidence | full для чтения и поиска |
26
+ | workspace operators / assignment | list/get/set | есть | read conversations / отдельный `write:operators` | CAS + повторный assignment get | full; CRM operators не смешиваются |
27
+ | message campaigns | waves/scheduler покрывают GetCourse/Wazzup flows | общего transport-neutral v2 adapter нет | campaign read/manage/spend | preview/status/analytics | partial; не смешивать с telephony campaigns |
28
+ | templates / audiences | Wazzup templates only | generic v2 API отсутствует | future campaign scopes | list/get | missing API, P2 |
29
+ | telephony | trigger actions и voice billing не равны управлению | trunks/numbers/calls API есть | read/write telephony | validate/get/call status | partial, P2 MCP definitions |
30
+ | dialogue topics | нет | v2 adapter отсутствует | future conversations read/write | list/get | missing API, P2 |
31
+ | satisfaction | нет | v2 adapter отсутствует | future analytics read | canonical UI query | missing API, P2 |
32
+ | arbitrary message send | нет | внутренние routes есть | внешний side effect | delivery receipt | intentional: не публиковать без отдельного auth/threat model |
33
+ | raw secrets / internal admin | нет | internal only | destructive/security | browser/admin audit | intentional |
34
+ | длительная задача MCP | create/list/get/update/close/delete | локальный owner, API не нужен | workspace-scoped local write | version CAS + fresh object reads | full на одной машине; межмашинного sync нет |
35
+
36
+ «9 analytics endpoints» не подтверждается контрактом как девять MCP-ready операций. После
37
+ проверки main содержит 12 read-only маршрутов incoming/funnel/AI-ledger, три маршрута custom
38
+ reports и отдельный набор broadcast analytics. Они имеют разные зерно, права и семантику и не
39
+ объединяются одним счётчиком.
40
+
41
+ ### Зависимости переноса аналитики в ветку v2
42
+
43
+ Ветка v2 проверена отдельно 2026-09-11. В ней отсутствуют доменные сервисы и HTTP-контракты,
44
+ которым делегируют main-адаптеры: `InboundAnalyticsService`, `FunnelAnalyticsService`,
45
+ `AiUsageService` и `CustomReportsService`. Поэтому main MCP definitions не копируются в v2 до
46
+ появления источника данных: оболочка без backend вернула бы пользователю неработающие tools.
47
+
48
+ | Операция main | Что требуется в v2 до переноса | Критерий готовности |
49
+ |---|---|---|
50
+ | incoming analytics | canonical summary/topics/speed/money/cycle/quality service + `/api/v2` adapter | одинаковые period/timezone filters и totals с UI fixture |
51
+ | funnel analytics | canonical meta/summary/cohorts service + `/api/v2` adapter | сохранены `view`, `grain`, cohort/period/entry и пустые интервалы |
52
+ | AI ledger | immutable usage ledger query + `/api/v2` adapter | `unpricedEvents` и unknown usage не превращаются в ноль |
53
+ | custom reports | definition list/get/run domain + `/api/v2` adapter | run использует сохранённую схему отчёта и возвращает canonical `grandTotal` |
54
+
55
+ Для каждого блока v2 дополнительно нужны `read:analytics` в auth/OpenAPI/runtime constants,
56
+ route-map generation, scope parity tests и protocol snapshot. До выполнения всех условий статус
57
+ v2 для этих операций — `missing API`, а не частичное покрытие.
58
+
59
+ ## Пагинация и полнота
60
+
61
+ | Класс list/read | Состояние | Правило клиента |
62
+ |---|---|---|
63
+ | ICP, sandbox runs, agents, variables, dialogues, audits | cursor | передавать query; идти до пустого cursor; page не равна парку |
64
+ | knowledge docs/QA/websites/tables | page + limit | сохранять page/total/hasMore API; не обрезать конвертом |
65
+ | files/tracked clicks | offset + limit | завершать по total/returned count |
66
+ | wave contacts | cursor/limit | contact page не заменяет весь run |
67
+ | CRM pipelines/stages | bounded provider/domain list, API cursor нет | не усекать; при превышении бюджета fail-closed |
68
+ | follow-up step stats/reactions | агрегат по шагам/типам, API cursor нет | не объявлять неизвестный total точным |
69
+ | datasource catalogs, Wazzup templates, feedback sheets | API cursor нет | технический gap; result не считать полным при `_mcpTruncated` |
70
+ | cohorts, schedules, mailing variants | API cursor нет | технический gap; сужать фильтрами, где они есть |
71
+
72
+ `assertCompleteList` запрещает использовать legacy unpaged ответ как полный, когда API вернул
73
+ cursor/hasMore, а caller не запросил страницу. GET-параметры строятся через `URLSearchParams` в
74
+ path; `ApiToolRequest` не имеет отдельного query и молча потерял бы параметры.
75
+
76
+ ## Замещающие записи
77
+
78
+ Четыре snapshot-класса защищены `neverTruncate`: trigger actions/platform context, agent variable
79
+ overrides, follow-up config и CRM field mapping. Agent/context имеют sections и detail tools.
80
+ Если полный снимок недоступен, MCP возвращает исполнимый отказ и не выполняет replace. Проверки
81
+ версии/`expected_content` сохраняются; page/sample/stale fingerprint не принимаются за полный
82
+ набор.
83
+
84
+ ## Принятые продуктовые решения
85
+
86
+ - Incoming/funnel analytics и AI ledger имеют отдельные API contracts и MCP tools. Broadcast
87
+ totals не используются как заменитель; unknown/unpriced остаются отдельными.
88
+ - Workspace operator queue строится по `PlatformWorkLog`, а не по текущему snapshot status.
89
+ - Generic templates/audiences, topics и satisfaction отложены до появления scoped v2 adapter.
90
+ - Telephony публикуется отдельным профилем после проверки recording/transcript PII и spend;
91
+ существующие телефонные trigger actions не считаются покрытием.
92
+ - Composite prompt edit не добавляется: одиночный `prompt_batch_start` уже даёт durable run,
93
+ idempotency, skippedCount и resume; сначала всё равно нужен полный prompt read.
94
+
95
+ ### Оставшиеся области интерфейса
96
+
97
+ Проверено по main API 2026-09-11. Решение «отложено» означает конкретную недостающую границу,
98
+ а не исчезновение области из MCP-плана.
99
+
100
+ | Область | Что уже есть | Чего не хватает | Решение сейчас | Условие пересмотра |
101
+ |---|---|---|---|---|
102
+ | message campaigns | GetCourse/Wazzup waves, cold welcome, scheduler, cohorts и broadcast analytics | единого v2 CRUD/lifecycle для transport-neutral кампании, шаблона и аудитории | не публиковать фиктивный generic tool; продолжать поддержанные специализированные маршруты | scoped v2 adapter с preview, idempotency, pause/cancel/status и delivery verification для каждого транспорта |
103
+ | templates | чтение Wazzup templates в рамках Wazzup wave | общего template list/get/CRUD API и канонической схемы переменных разных каналов | оставить Wazzup-чтение внутри его маршрута | v2 API отделяет channel template от message body, проверяет approval/media/variables и даёт безопасный preview |
104
+ | audiences | файловые когорты и контакты конкретных wave/import flows | самостоятельного audience list/get/definition API с доказуемой полнотой нет | не выдавать импортный файл за сохранённую аудиторию | cursor API, источник/версия выборки, expected count и повторная проверка перед spend |
105
+ | telephony | v2 API для trunks/numbers/calls/call lists/voice agents/telephony campaigns; trigger-actions могут начать звонок | отдельной MCP-поверхности и проверенного профиля для recording/transcript PII, spend и feature flags нет | отложить как самостоятельный профиль; телефонные trigger-actions не считать полным покрытием | threat model, scopes `read:telephony`/`write:telephony`, redaction/retention, spend confirmation и lifecycle eval готовы вместе |
106
+ | dialogue topics | внутренний v1 CRUD, channel assignment и async backfill | scoped `/api/v2` adapter, MCP read/write и status/cancel contract отсутствуют | отложить, не вести модель к внутреннему URL | v2 read/write scopes, cursor list, channel ownership и retained backfill lifecycle |
107
+ | satisfaction | внутренние v1 summary/list и capture/dispatch domain | scoped `/api/v2` analytics adapter и MCP read contract отсутствуют | отложить до read-only adapter; автоматический dispatch наружу не публиковать | period/timezone filters, response-mode/status semantics и unknown totals совпадают с UI fixtures |
108
+
109
+ ## Проверка параметров вариантов аналитики
110
+
111
+ `besales_funnel_analytics_get`: meta принимает только platform, cohorts не принимает view
112
+ (всегда cohort), summary поддерживает view. `besales_ai_costs_get`: group_by/page/limit
113
+ доступны только для breakdown. Неприменимые параметры отвергаются до HTTP; фильтр не
114
+ отбрасывается молча. Проверка: `src/tools/definitions/analytics.spec.ts`.
@@ -0,0 +1,46 @@
1
+ # Политика PII в Besales MCP
2
+
3
+ Дата инвентаризации: 2026-09-11. Секреты и PII — разные контуры: browser handoff не отдаёт
4
+ credentials модели, но разрешённые рабочие данные могут оказаться в транскрипте хоста.
5
+
6
+ ## Инвентарь
7
+
8
+ | Поверхность | Возможные данные | Зачем нужны | Политика |
9
+ |---|---|---|---|
10
+ | dialogue query/transcript | имя, username/телефон, сообщения, CRM-ссылки | аудит, диагностика, ICP | list по умолчанию краткий; полный transcript только явным get и под `read:conversations` |
11
+ | ICP dialogue samples | реплики, outcome, признаки контакта | анализ сегментов и скрипта | import с anonymize по умолчанию; raw источник не копировать в task state |
12
+ | wave contacts / run contacts | contact ID, messenger, статус доставки, причина отказа | dry-run, восстановление рассылки | scope кампаний; страницы; не эхоить весь вход после записи |
13
+ | findings/evidence | выдержки сообщений, оценки, scenario evidence | объяснить качество | возвращать минимальный фрагмент, нужный для вывода; полный источник получать точечно |
14
+ | link clicks | имя контакта, contact/message IDs, время | атрибуция | описание инструмента явно предупреждает о PII; отдельный scope conversations |
15
+ | workspace labels | название компании, email владельца | выбор подключения | email только маскированный; label cache не хранит raw email |
16
+ | API keys/webhook secrets | credentials | подключение канала | только одноразовый browser reveal; рекурсивно удаляются из MCP result/log |
17
+ | structured errors | operation/object ID, service account email, remediation | продолжение после сбоя | whitelist; неизвестные поля и payload бэкенда наружу не проходят |
18
+
19
+ ## Правила
20
+
21
+ 1. Tenant/workspace/object ownership проверяет API. Локальный MCP не расширяет scope и не
22
+ объединяет права нескольких ключей.
23
+ 2. Поиск и list возвращают идентичность и краткое состояние. Текст диалога, полный prompt,
24
+ evidence и контакты читаются отдельным явным вызовом.
25
+ 3. Для анализа без нужды в идентичности используется анонимизация/псевдонимизация. Когда задача
26
+ требует доставку конкретному человеку или разбор конкретного сообщения, удаление ID разрушило
27
+ бы разрешённую функцию; доступ ограничивается scope и минимальным ответом.
28
+ 4. MCP не пишет raw dialogue, contact list или findings в локальный progress/task payload.
29
+ Допустимы только IDs созданных сущностей, агрегаты, checkpoints и согласованные ограничения.
30
+ 5. Host transcript и telemetry находятся под retention-политикой выбранного хоста. MCP не может
31
+ удалить уже переданный ему текст; поэтому перед полным чтением описания предупреждают о PII,
32
+ а оператор выбирает корпоративный host/retention. Экспорт в сторонний файл выполняется только
33
+ по явной просьбе.
34
+ 6. Ошибка не должна удалять полезную безопасную диагностику: `serviceAccountEmail`, operation ID,
35
+ missing scope и remediation сохраняются. Сырые stack, токены, webhookSecret, apiKey и headers
36
+ удаляются.
37
+ 7. Локальные логи маскируют email/телефоны/ключи; тесты проверяют вложенные объекты, массивы и
38
+ ошибки, а не только top-level поля.
39
+
40
+ ## Retention и удаление
41
+
42
+ API-источники живут по профильным retention/GDPR правилам домена. MCP resources и tool schemas
43
+ не содержат customer PII. Keychain хранит credentials до disconnect/revoke. Кэш подключения
44
+ хранит только workspace ID/name, маскированный account label, scopes и timestamps. Потерянный
45
+ browser reveal не восстанавливается из MCP-транскрипта: пользователь выпускает новый credential
46
+ только осознанно, понимая отзыв прежнего.
@@ -0,0 +1,150 @@
1
+ # Бюджет размера ответов
2
+
3
+ Сколько на самом деле весят ответы инструментов, какой потолок из этого следует и почему
4
+ у части инструментов потолка нет вовсе.
5
+
6
+ Единица везде — **байты UTF-8** (`Buffer.byteLength`). Считать длину строки нельзя:
7
+ `String.length` меряет единицы UTF-16, и на кириллице ошибается вдвое.
8
+
9
+ ## Зачем это нужно
10
+
11
+ Ответ инструмента целиком попадает в контекст модели, которая его вызвала. Один ответ
12
+ на несколько сотен килобайт вытесняет из контекста всё остальное — включая инструкции,
13
+ по которым модель должна была работать. До замера потолок назначать нельзя: цена байта
14
+ в токенах различается **в 2,5 раза** (плотный список идентификаторов ≈ 2 байта на токен,
15
+ русский текст ≈ 4–5), поэтому «круглое число на глаз» означает либо мёртвый предохранитель,
16
+ либо срабатывание на штатных ответах.
17
+
18
+ ## Как замерялось
19
+
20
+ Прогон только на чтение по инструментам с методом GET на четырёх живых воркспейсах,
21
+ 190 успешных ответов. Инструменты, чей адрес в v2 переехал и на живом бэкенде не отвечает,
22
+ измерялись отгруженным мостом: данные те же, меняется только адрес.
23
+
24
+ Из прогона исключены два GET-инструмента, которые вопреки методу меняют состояние:
25
+ проверка живости источника данных (успех переводит источник в активное состояние) и
26
+ список операторов CRM с включённой пересинхронизацией.
27
+
28
+ ## Распределение
29
+
30
+ | Доля ответов | Вес |
31
+ |---|---|
32
+ | половина | до 1 КБ |
33
+ | три четверти | до 6 КБ |
34
+ | 90 % | до 31 КБ |
35
+ | 95 % | до 51 КБ |
36
+ | 99 % | до 620 КБ |
37
+ | максимум | **7,96 МБ** |
38
+
39
+ Компактный JSON против отформатированного: медиана **−16 %**, на самых тяжёлых ответах
40
+ до **−35 %**. Экономия реальная, но она не спасает: ответ на 8 МБ остаётся неработоспособным
41
+ и после сжатия.
42
+
43
+ ## Самые тяжёлые ответы
44
+
45
+ | Инструмент | Максимум | Из чего состоит |
46
+ |---|---|---|
47
+ | снимок канала (`platform_context_get`) | **7 959 506** | 40 агентов × ~197 КБ; у каждого системный промпт 143 КБ (72 % веса) и триггеры 54 КБ |
48
+ | аналитика рассылок (`broadcast_analytics_get`) | **1 340 671** | `data` 897 КБ + `totals` 437 КБ |
49
+ | когорты рассылок (`broadcast_cohorts_list`) | **634 625** | `items` 634 КБ, постранично не разбивается |
50
+ | сценарии ICP (`icp_scenarios_list`) | 119 181 | голый массив без конверта |
51
+ | карта полей CRM (`crm_field_mapping_get`) | 77 597 | `available_fields` 77 КБ, а сам снимок для записи (`current_mappings`) — 2 байта |
52
+ | воронки CRM (`crm_pipeline_list`) | 74 523 | `items` |
53
+ | список агентов (`agent_list`) | 73 859 | `data` |
54
+ | кандидаты оплат | 52 348 | `items`, 30 строк |
55
+ | вопросы-ответы базы знаний | 42 793 | `qa_pairs` |
56
+ | выборка диалогов | 33 747 | `data`, 50 строк |
57
+ | стенограмма диалога | 33 672 | `turns` 18,8 КБ **и** `transcriptMarkdown` 11,3 КБ — одно и то же содержимое дважды |
58
+
59
+ Ресурсы (справочники, отдаются целиком одним чтением): 37 файлов, медиана 4 КБ,
60
+ максимум **104 836** байт — принципы промптинга. Инструкции сервера, уходящие при каждом
61
+ подключении, после выноса подробностей в resources/help — 5 112 байт (3 278 символов).
62
+
63
+ ## Три класса и потолки
64
+
65
+ ### 1. Обычные ответы — 64 КБ
66
+
67
+ Покрывает 95 % замеренных ответов с запасом. Всё, что не попало в два класса ниже.
68
+
69
+ ### 2. Отчёты, которые можно сузить — 64 КБ с выбрасыванием строк
70
+
71
+ Аналитика и когорты рассылок. У обоих есть параметры сужения (период, канал, срез),
72
+ поэтому подсказка «сузьте запрос» — правда, а не выдумка. Выбрасываются именно строки
73
+ данных, по пути, а не «самое тяжёлое поле».
74
+
75
+ ### 3. Снимки для замещающей записи — потолка нет, есть отказ
76
+
77
+ Четыре чтения, чей ответ модель отправляет обратно как «полный набор»:
78
+
79
+ | Чтение | Запись, замещающая набор целиком |
80
+ |---|---|
81
+ | снимок канала | правка триггера с заменой действий |
82
+ | переопределения переменных агента | обновление агента |
83
+ | список цепочек дожима с конфигом | сохранение цепочки |
84
+ | карта полей CRM | сохранение карты с заменой |
85
+
86
+ Молча усечённый снимок здесь означает **безвозвратную потерю**: модель добросовестно
87
+ пришлёт назад неполный набор, и непрочитанное будет удалено. Автоматического возврата
88
+ у этих операций нет. Поэтому обрезка запрещена, а при превышении возвращается отказ
89
+ с указанием, чем сузить запрос.
90
+
91
+ Тот же запрет — на инструменты, у которых постраничная выдача уже работает
92
+ (стенограмма, выборка диалогов, контакты волны): конверт подменил бы исправный механизм
93
+ тихой потерей.
94
+
95
+ **Порог отказа — 256 КБ.** Он выше наблюдаемого максимума работающих снимков (77,6 КБ),
96
+ чтобы предохранитель не гасил живые сценарии.
97
+
98
+ ## Что теперь лечится на стороне API
99
+
100
+ **Снимок канала на 8 МБ.** Это не выброс, а штатный ответ для воркспейса с сорока
101
+ агентами на одном канале. 72 % веса — системные промпты агентов, которые в этом сценарии
102
+ не нужны вовсе: их читают отдельными инструментами промптов. Но даже без промптов
103
+ остаётся 2,2 МБ триггеров.
104
+
105
+ Поэтому `besales_platform_context_get` теперь принимает `sections`: для выбора достаточно
106
+ `platform,agent_summaries,readiness`, а router/behavior/follow_up/pipeline_settings читаются
107
+ отдельно. `besales_agent_list` отдаёт компактные summaries с поиском и пагинацией. Полный
108
+ `compiledPrompt` появляется только в явном `besales_agent_get` или секции `agents_full`.
109
+ Замещающая правка по-прежнему требует полного снимка относящейся к ней секции.
110
+
111
+ ## Как это включается
112
+
113
+ Потолок включается переменной окружения `BESALES_MCP_RESULT_ENVELOPE=on` и по умолчанию
114
+ **выключен**. Причина — установки: пакет рекомендуется запускать без пина версии, поэтому
115
+ правка доезжает до всех сама, посреди рабочих сессий. Один релиз выключенным, включение —
116
+ следующим. Компактная сериализация работает всегда: она меняет только пробелы.
117
+
118
+ В том же совместимом флаге включается MCP `outputSchema`. При включении каждый успешный
119
+ ответ получает одинаковый JSON в `content[].text` и `structuredContent`; массив
120
+ оборачивается в `{ "items": [...] }`, скаляр — в `{ "value": ... }`. Поэтому SDK-хост
121
+ проверяет реально возвращаемую форму, а старые клиенты до переключения флага продолжают
122
+ получать прежний текстовый контракт.
123
+
124
+ Путь после успешного API-ответа считается уже состоявшейся операцией. Если mapper,
125
+ политика размера или сериализация ломаются после ответа на мутацию, MCP возвращает
126
+ неошибочную квитанцию с доступными `id`/`operationId`/`runId`/`jobId` (включая вложенные
127
+ поля) и маркером `_mcpPostprocessingFailed` либо `_mcpFormattingFailed`. Повторять такую
128
+ мутацию нельзя. У чтения ошибка постобработки остаётся обычной ошибкой и допускает новый
129
+ вызов.
130
+
131
+ Политика объявляется у самого инструмента:
132
+
133
+ - `neverTruncate` — обрезать нельзя, при превышении отказ;
134
+ - `droppablePaths` — что разрешено выбросить, путём (`agents[].baseSystemPrompt` снимает
135
+ поле у каждого элемента массива); объявляется только для того, что читается **отдельным**
136
+ инструментом, иначе это потеря, а не сжатие;
137
+ - `maxResultBytes` — свой потолок инструмента;
138
+ - `narrowingHint` — чем сузить запрос; ставится только там, где сужать действительно есть чем.
139
+
140
+ Списка «неприкосновенных путей» намеренно нет: выбрасывается ровно перечисленное, всё
141
+ остальное неприкосновенно по построению. Второй список был бы полем, которое никто не читает.
142
+
143
+ Маркеры в ответе начинаются с `_mcp`: у бэкенда есть собственные признаки усечения с другим
144
+ смыслом, и совпадение имён их бы перемешало.
145
+
146
+ ## Когда пересматривать
147
+
148
+ Если предохранитель начнёт срабатывать на штатных ответах — **поднимать потолок, а не
149
+ выключать проверку**. Перед изменением чисел прогон замера повторяется: числа выше
150
+ получены на конкретных воркспейсах и стареют вместе с данными.