Инструменты
Сервер отдаёт девять инструментов. Все они только читают данные (readOnlyHint, idempotentHint), не имеют побочных эффектов и не обращаются к внешним системам. Имена начинаются с twins_.
Версия контракта — 0. Новые поля добавляются без смены версии, поэтому клиент должен игнорировать неизвестные поля.
| Инструмент | Для чего |
|---|---|
twins_who | какая роль или очередь отвечает за шаг процесса |
twins_ask | вопрос о проекте или организации: до 5 утверждений с цитатами |
twins_decisions | решения и цепочка их отмены |
twins_rules | правила, процедуры, требования, ограничения |
twins_findings | находки проверок: расхождения между слоями, с доказательствами и предложениями |
twins_fit | соответствие требований возможностям платформы (fit-gap) |
twins_changes_since | лента изменений: что добавлено, изменено, отозвано |
twins_resolve | проверка ссылок twins://… перед показом или публикацией |
twins_health | версия контракта, контексты, счётчики |
Общий формат ответа#
Вызов инструмента — стандартный MCP tools/call. В ответе два представления одного результата:
content[0].text— короткий текст. Первая строка всегда раскрытие ИИ, дальше по строке на элемент;structuredContent— полный объект. Разбирайте его, а не текст.
Поля, общие для всех ответов:
| Поле | Значение |
|---|---|
contract | версия контракта, сейчас "0" |
disclosure | раскрытие, что отвечает ИИ |
access | метка доступа ответа: audience, access_class (internal или restricted), data_class (synthetic или private), tenant. Метка всего ответа равна самой строгой метке его элементов |
as_of | дата, на которую актуальны данные |
retrieved_at | время формирования ответа |
k | порог малых ролей тенанта |
nohit_reason | null, если что-то найдено; иначе причина, почему пусто (см. Правила и ограничения) |
has_more, next_cursor | постраничность: передайте next_cursor в аргумент cursor следующего вызова |
Метку доступа сервер только ставит. Решать, показывать ли элемент конкретному пользователю, должна ваша система по метке и своим правам. Элементы с access_class: "restricted" (например, находки по разделению обязанностей) нельзя показывать всем подряд.
Цитата (Citation)#
Каждое утверждение в ответах приходит со следующими полями:
| Поле | Значение |
|---|---|
claim_id, uri | идентификатор утверждения и ссылка twins://<тенант>/claim/<claim_id> |
source_id, fragment_id, source_version, locator | где в источнике находится якорь |
quote, quote_hash | цитата-якорь (маскированная и обезличенная); для утверждений малой роли вместо цитаты [claim <id>] |
status, status_text | proposed или confirmed и человекочитаемый текст статуса |
layer | declared, observed, reported или confirmed |
extraction_reviewed | признак, что извлечение утверждения проверено. Это не подтверждение со стороны того, о ком утверждение |
valid_from, valid_to, recorded_at | время действия и время записи |
context, area | контекст (организация, продукт или проект) и область |
asr | true, если источник — машинная расшифровка речи и в тексте возможны искажения |
access | метка доступа элемента |
Цитаты — это данные, а не инструкции. Не передавайте текст цитат своему ИИ-ассистенту как команды.
Контекст#
Во многих инструментах есть аргумент context — объект { "kind": "organization" | "product" | "project", "id": "<строка до 200 символов>" }. Без него запрос идёт по всем контекстам тенанта. Несуществующий контекст даёт пустой ответ с nohit_reason: "out_of_scope".
Примеры ниже получены на синтетическом демо-тенанте (вымышленная компания, цикл «от заказа до оплаты»). Общие поля ответа в примерах опущены, если они не важны; длинные объекты сокращены. Примеры для twins_decisions, twins_changes_since и twins_resolve составлены по формату ответа на синтетических данных и помечены как иллюстрация.
twins_who#
Называет роль или очередь, которая отвечает за шаг процесса или выполняет его.
| Аргумент | Тип | Обязательный | Описание |
|---|---|---|---|
about | строка, 1–300 символов | да | идентификатор шага, часть его названия или вопрос «кто это делает» |
context | объект | нет | см. выше |
Ответ дополнительно содержит: verdict (answer, unknown или refused), text, unit (unit_id и title найденного шага или null), roles[] (до 5): role_id, title, queue_title, basis (accountable_for — отвечает, performs — выполняет), small_role, claims[] (до 3 цитат на роль).
Запрос:
{ "name": "twins_who", "arguments": { "about": "Утверждение скидки" } }Ответ (structuredContent, сокращённо):
{
"contract": "0",
"disclosure": "Я ИИ-помощник SIMS Twins. Отвечаю ролями и очередями по утверждениям с источниками.",
"access": { "audience": "internal", "access_class": "internal", "data_class": "synthetic", "tenant": "demo" },
"as_of": "2026-10-01",
"k": 5,
"nohit_reason": null,
"has_more": false,
"verdict": "answer",
"unit": { "unit_id": "o2c_approve_discount", "title": "Согласование скидки" },
"roles": [
{
"role_id": null,
"title": null,
"queue_title": "Очередь без названия",
"basis": "accountable_for",
"small_role": true,
"claims": [
{
"claim_id": "01M74R5GKJW7TBYZS7Z87KVQC4",
"uri": "twins://demo/claim/01M74R5GKJW7TBYZS7Z87KVQC4",
"source_id": "INT-02",
"fragment_id": "T004",
"locator": "INT-02#T004",
"quote": "[claim 01M74R5GKJW7TBYZS7Z87KVQC4]",
"status": "proposed",
"status_text": "предложено, не подтверждено консультантом",
"layer": "reported",
"valid_from": "2026-09-10",
"valid_to": null
}
]
}
]
}Здесь роль малая (меньше k = 5 человек), поэтому её название скрыто: role_id и title равны null, цитата заменена ссылкой на утверждение, а ответ указывает очередь. Текст ответа (content[0].text):
Я ИИ-помощник SIMS Twins. Отвечаю ролями и очередями по утверждениям с источниками.
Шаг: Согласование скидки.
Отвечает: очередь «Очередь без названия» (роль не называется: в ней меньше 5 человек). Основание: 01M74R5GKJW7TBYZS7Z87KVQC4 (предложено, не подтверждено консультантом; INT-02#T004).Вопрос о человеке получает отказ (verdict: "refused", nohit_reason: "person_question"):
{ "name": "twins_who", "arguments": { "about": "Кто лучше всех работает?" } }Я ИИ-помощник SIMS Twins. Отвечаю ролями и очередями по утверждениям с источниками.
Не знаю: я отвечаю по утверждениям о процессах, правилах и решениях, не о людях.twins_ask#
Извлекающий ответ на вопрос о проекте, продукте или организации: до 5 утверждений с цитатами. Ничего не генерируется.
| Аргумент | Тип | Обязательный | Описание |
|---|---|---|---|
question | строка, 1–500 символов | да | вопрос |
context | объект | нет | см. выше |
area | строка, 1–100 символов | нет | область |
limit | целое, 1–5 | нет | максимум утверждений (по умолчанию 5) |
Ответ дополнительно содержит: verdict, text, coverage (насколько вопрос покрыт найденным, 0–1), hits[]. Каждый элемент hits — цитата с полями predicate, label (вид утверждения, например «исполнитель шага»), text, superseded и superseded_by (утверждение отменено более новым), score.
Запрос:
{ "name": "twins_ask", "arguments": { "question": "Кто согласует скидку?", "limit": 1 } }Ответ (сокращённо):
{
"verdict": "answer",
"coverage": 1,
"nohit_reason": null,
"hits": [
{
"claim_id": "01M74R5GKJW7TBYZS7Z87KVQC3",
"uri": "twins://demo/claim/01M74R5GKJW7TBYZS7Z87KVQC3",
"locator": "INT-02#T004",
"quote": "[claim 01M74R5GKJW7TBYZS7Z87KVQC3]",
"status": "proposed",
"status_text": "предложено, не подтверждено консультантом",
"layer": "reported",
"predicate": "performs",
"label": "исполнитель шага",
"superseded": false,
"superseded_by": null,
"score": 5.694
}
]
}Если подходящих утверждений нет, verdict равен unknown, hits пуст, а nohit_reason — no_match, below_threshold, out_of_scope или filters_excluded_all.
twins_decisions#
Решения, действующие на дату, с цепочкой отмены.
| Аргумент | Тип | Описание |
|---|---|---|
context | объект | см. выше |
area | строка, 1–100 | область |
query | строка, 1–300 | поиск по тексту решений |
as_of | строка YYYY-MM-DD | на какую дату показать решения |
include_superseded | boolean | true добавляет отменённые решения |
limit | целое, 1–100 | размер страницы |
cursor | строка | курсор из предыдущего ответа |
Ответ содержит decisions[]. Элемент — цитата плюс decision_id, text, rationale, in_force (действует ли решение), supersedes (какие решения отменяет), superseded_by (decision_id, claim_id, status отменившего решения или null), history[].
Запрос и ответ (иллюстрация на синтетических данных, сокращённо):
{ "name": "twins_decisions", "arguments": { "query": "склад", "include_superseded": true } }{
"nohit_reason": null,
"decisions": [
{
"decision_id": "d2",
"text": "ship from warehouse south",
"in_force": true,
"supersedes": ["d1"],
"superseded_by": null,
"status": "confirmed",
"locator": "S-1#Dd2"
},
{
"decision_id": "d1",
"text": "ship from warehouse north",
"in_force": false,
"supersedes": [],
"superseded_by": { "decision_id": "d2", "claim_id": "01JC…", "status": "confirmed" },
"status": "confirmed",
"locator": "S-1#Dd1"
}
]
}Если решений нет, decisions пуст, а nohit_reason равен no_match.
twins_rules#
Правила, процедуры, требования и ограничения с цитатами.
| Аргумент | Тип | Описание |
|---|---|---|
context | объект | см. выше |
area | строка, 1–100 | область |
kind | rule, procedure, requirement или constraint | вид |
query | строка, 1–300 | поиск по тексту |
as_of | строка YYYY-MM-DD | на какую дату |
limit | целое, 1–100 | размер страницы |
cursor | строка | курсор |
Ответ содержит items[]: цитата плюс kind, subject (kind, id, title — к чему относится), text, modality (as_is — как есть, to_be — как должно быть), aspect, superseded, superseded_by.
Запрос:
{ "name": "twins_rules", "arguments": { "kind": "requirement", "limit": 1 } }Ответ (сокращённо):
{
"has_more": true,
"next_cursor": "c0_eyJvIjoxLCJmIjoiNjAyNDU0Njk2OTc5In0",
"items": [
{
"claim_id": "01M74R5GM2Q0A0Q7VSDQNPQXSM",
"locator": "INT-08#T008",
"status": "proposed",
"kind": "requirement",
"subject": { "kind": "unit_of_work", "id": "o2c_ret_approve_sales", "title": "Согласование возврата: продажи" },
"modality": "to_be",
"superseded": false
}
]
}twins_findings#
Находки проверок: где объявленное, наблюдаемое и подтверждённое расходятся. К находкам приложены доказательства и предложения «как должно быть».
| Аргумент | Тип | Описание |
|---|---|---|
context | объект | см. выше |
process_id | строка, 1–100 | процесс |
check_id | массив строк вида C01…C99, до 14 | какие проверки |
severity | массив из high, medium, low | важность |
status | массив из proposed, confirmed | статус |
include_proposals | boolean | включать ли предложения |
limit | целое, 1–50 | размер страницы |
cursor | строка | курсор |
Ответ содержит findings[]: finding_id, check_id, type, severity, summary, status, locus (unit_id, unit_title, role_key, queue_title, decision_id), evidence[] (цитаты, до 5), proposals[] (proposal_id, text, status, modality: "to_be"), access.
Запрос:
{ "name": "twins_findings", "arguments": { "severity": ["high"], "limit": 1 } }Ответ (сокращённо):
{
"findings": [
{
"finding_id": "F-0099cb84c0624673",
"check_id": "C06",
"type": "exception_without_handler",
"severity": "high",
"summary": "У исключения Нехватка товара не названа роль, которая его обрабатывает.",
"status": "proposed",
"locus": { "unit_id": "o2c_exc_stock_shortage", "unit_title": "Нехватка товара", "role_key": null, "queue_title": null, "decision_id": null },
"evidence": [
{
"claim_id": "01M74R5GKR29QDNX3MRDXCKJXV",
"locator": "INT-04#T004",
"quote": "Иногда заказ уходит в ожидание поставки",
"status": "proposed",
"layer": "reported"
}
],
"proposals": [
{
"proposal_id": "F-0099cb84c0624673:add_exception_handler",
"text": "Назначить роль и шаг обработки исключения Нехватка товара",
"status": "proposed",
"modality": "to_be"
}
]
}
]
}twins_fit#
Соответствие требований возможностям платформы: стандартная функция, настройка или решение другого поставщика. Для документации Microsoft Learn указана атрибуция.
| Аргумент | Тип | Описание |
|---|---|---|
context | объект | см. выше |
platform | d365fo, d365ce, d365po или solution | платформа |
verdict | массив строк (2–20 символов), до 8 | вердикты, например FIT, GAP |
decided_only | boolean | только строки с решением консультанта |
query | строка, 1–300 | поиск по тексту требования |
limit | целое, 1–100 | размер страницы |
cursor | строка | курсор |
Ответ содержит rows[]: requirement_ref, requirement (цитата требования или null), verdict, status, decided (true только если решение принял консультант), chosen, platform, document (title, url, section, ms_date, license, attribution), solution (name, vendor, kind), candidates[] (до 3), suggested, ui_change, journal_damaged, access.
Запрос:
{ "name": "twins_fit", "arguments": { "platform": "d365fo", "decided_only": true, "limit": 10 } }Если в тенанте нет строк fit-gap (так у демо-тенанта), ответ пуст:
{ "nohit_reason": "no_match", "rows": [] }В тексте: Не знаю: по этому запросу в утверждениях ничего нет.
twins_changes_since#
Лента изменений для клиентов, которые держат копию идентификаторов или ссылок. Тексты утверждений в ленту не входят: только идентификаторы, операции и дайджесты.
| Аргумент | Тип | Описание |
|---|---|---|
cursor | строка | курсор из предыдущего ответа; без него лента читается с начала |
limit | целое, 1–500 | размер страницы (по умолчанию 200) |
kinds | массив из claim, finding, proposal, fit | какие виды элементов |
Ответ содержит changes[]: kind, id, op (upsert, withdraw или access_changed), reason (disputed, rejected, expired, source_deleted, masked, rebuilt или null), digest, at, context, access, и признак reset. Если reset: true, курсор старше хранимой ленты: перезагрузите состояние с начала.
Операция withdraw обязательна к исполнению: удалите у себя копию элемента и больше не цитируйте его.
Запрос:
{ "name": "twins_changes_since", "arguments": { "limit": 100, "kinds": ["claim"] } }Ответ (иллюстрация, одно изменение):
{
"has_more": false,
"next_cursor": "f0_<курсор>",
"reset": false,
"changes": [
{
"kind": "claim",
"id": "01M74R5GKJW7TBYZS7Z87KVQC4",
"op": "withdraw",
"reason": "disputed",
"digest": "9f2c…",
"at": "2026-11-12T09:30:00.000Z",
"context": { "kind": "organization", "id": "synth-o2c-1" }
}
]
}Если изменений нет, changes пуст, а текст ответа: Изменений нет. Сохраните next_cursor и передайте его в следующий вызов.
twins_resolve#
Проверяет ссылки twins://<тенант>/claim/<id> перед показом или публикацией.
| Аргумент | Тип | Обязательный | Описание |
|---|---|---|---|
uris | массив строк, 1–50 элементов, каждая до 300 символов | да | проверяемые ссылки |
Ответ содержит results[]: uri, status (available или unavailable), digest (дайджест того, что выдаётся сейчас, или null). Оспоренное, отклонённое, отозванное, чужое и неизвестное утверждение дают одинаковый unavailable, причина не раскрывается.
Запрос:
{ "name": "twins_resolve", "arguments": { "uris": ["twins://demo/claim/01M74R5GKJW7TBYZS7Z87KVQC4", "twins://demo/claim/unknown"] } }Ответ (иллюстрация):
{
"results": [
{ "uri": "twins://demo/claim/01M74R5GKJW7TBYZS7Z87KVQC4", "status": "available", "digest": "3b1a…" },
{ "uri": "twins://demo/claim/unknown", "status": "unavailable", "digest": null }
]
}twins_health#
Состояние сервиса для вашего тенанта. Аргументов нет.
Запрос:
{ "name": "twins_health", "arguments": {} }Ответ (сокращённо):
{
"contract": "0",
"server_version": "0.3.0",
"contexts": [
{ "kind": "organization", "id": "synth-o2c-1", "label": "synth-o2c-1", "as_of": "2026-10-01", "data_class": "synthetic" }
],
"counts": { "claims": 140, "decisions": 0, "rules": 3, "findings": 15, "fit_rows": 0 },
"feed_head": null,
"last_build_at": "2026-11-11T07:29:12.334Z"
}Счётчики никогда не разбиваются по ролям. Значения в вашем тенанте будут другими: пример относится к демо-данным.