Ассистенты (MCP)¶
Пайплайн — это JSON, а JSON языковая модель писать умеет. Чего она не умеет — знать, примет ли написанный граф ваш бэкенд: стадии ваши, типы узлов — у вашего ядра, а потолки принадлежат тому, кто вызывает.
stageflow-mcp — эта недостающая половина: MCP-сервер, который говорит на тех же семи эндпоинтах, что и редактор, так что ассистент может проверить граф на настоящем бэкенде, запустить его и прочитать, что вышло.
Вашему бэкенду менять ничего не нужно. Это второй клиент контракта, который вы уже обслуживаете, и он работает против развёртывания, сделанного раньше, чем он появился.
Как подключить¶
Адрес — тот же, что вводится на экране подключения редактора, и читается так
же: localhost:8765 и …/api означают один бэкенд. Голому имени хоста
подставляется https://, кроме loopback.
Проверить до того, как привязывать ассистента:
$ stageflow-mcp --backend https://ваш-бэкенд --check
backend https://ваш-бэкенд
credential none sent
stages 23
core 0.13.0
plan demo (open)
node types condition, entry, map, parallel, stage, subpipeline, switch, terminal, try
limits {"counters": {"seconds": 30, "steps": 300, …}}
Ключ, если бэкенд его хочет¶
Это тот же ключ, что носит редактор, в том же заголовке, и выдаёт его не этот инструмент: ядро не знает, что такое токен (Чей это запрос), так что как он выглядит — решаете вы.
stageflow-mcp --backend https://… --token "$TOKEN"
stageflow-mcp --backend https://… --token "$TOKEN" --auth-header X-Api-Key
Без ключа вызывающий — то, что ваш бэкенд называет анонимным посетителем.
Если --backend не указан вовсе, первый же инструмент, которому нужен адрес,
спросит его через собственный запрос MCP-клиента и запомнит ответ. Ключ никуда
не записывается, и инструмента, принимающего ключ, намеренно нет: аргумент
инструмента пишет модель, а значит он попадает в переписку и в логи клиента.
Что получает ассистент¶
| Инструмент | |
|---|---|
validate_pipeline |
все нарушения разом — схема, граф, объявленные типы и то, что запрещает план вызывающего. Прогона не стоит. |
run_pipeline |
запускает и отдаёт кадр переменных в конце, счётчики против ваших потолков и путь, которым прогон прошёл |
stop_run |
останавливает идущий прогон |
| Ресурс | |
|---|---|
stageflow://guide |
как пишется граф: кадр, типы узлов, выражения |
stageflow://stages |
ваши стадии, по строке на каждую |
stageflow://stages/{name} |
одна стадия целиком — типы, необязательность, что значит каждый аргумент |
stageflow://capabilities |
типы узлов, план и потолки, из /api/meta |
stageflow://schema |
JSON Schema пайплайна, с вашими именами стадий и типами узлов в виде enum |
Порядок работы, которого просит сервер: написать, позвать validate_pipeline,
починить всё, что он перечислил, повторить — и только потом запускать. Проверка
бесплатна, так что тратить прогон на выяснение опечатки в имени поля незачем.
Почему проверка не стоит прогона¶
Эндпоинта /api/validate в контракте нет, и он не требуется. POST /api/run с
mode: "step" разбирает граф и проверяет его политикой вызывающего до того,
как допустит прогон, — оба референсных бэкенда делают это именно в таком
порядке, раньше, чем появятся поток, слот и id. Негодный граф отклоняется
бесплатно; годный встаёт перед первым узлом, не исполнив ничего, и сразу
гасится.
То есть проверка на мгновение занимает один слот прогонов вызывающего и не исполняет ничего. Если ваш бэкенд допускает прогон раньше проверки — об этом стоит знать и по другим причинам.
Что с этим можно сделать¶
Обязательного — ничего. Две вещи того стоят.
Отвечать на языке вызывающего. Ассистент передаёт Accept-Language; без
этого собственные сообщения ядра приходят на том языке, на котором написаны
(Локализация):
from stageflow import i18n
asked = request.headers.get("accept-language", "")
with i18n.use_locale(i18n.negotiate(i18n.parse_accept_language(asked))):
...
Держать ядро свежим. Отказ называет узел, к которому относится, и перечисляет все нарушения разом (Ошибки), а это разница между списком, который ассистент проходит по пунктам, и фразой, которую он вынужден разгадывать:
guard.except[0]: 'next' is a required property;
risky.arguments: Additional properties are not allowed ('oops' was unexpected)
Мост к открытому редактору¶
печатает ссылку. Откройте её — и редактор смотрит на тот же граф, что и ассистент: что он рисует, попадает на холст, что вы меняете там, читается обратно. Приходящий граф сохраняет вашу камеру и положение всех карточек, которые вы перетащили, помечает тронутое и ждёт, пока его оставят или вернут.
Мост — это loopback-сокет на машине ассистента, под токеном, и ваш бэкенд в нём
не участвует: разве что origin редактора должен быть разрешён, а он уже
разрешён, если редактором кто-то пользуется. Одно правило браузера знать стоит:
страница из интернета, стучащаяся на 127.0.0.1, у нынешнего Chrome проходит
через разрешение пользователя. Разрешите — либо отдавайте редактор с той же
машины (--editor http://127.0.0.1:8080/), и тогда ничего никуда не пересекает.
Чего он не делает¶
- Ввод. Стадия в ядре умеет ждать ввода, но в HTTP-контракте канала для него нет — у редактора тоже, — так что ждущий граф отвалится по таймауту (Управление сессией).
- Пошаговую отладку. Шаг существует, чтобы человек смотрел, как идёт граф; ассистенту, который не смотрит, он только держал бы слот.
- Ничего локально. В этом процессе ничего не проверяется и не исполняется. Семантика живёт в ядре, которое крутит ваш бэкенд, а второе мнение было бы вторым воплощением, которому предстоит с ним разойтись.