# Один движок для ручной работы, MCP и Blender **Принято 18.09.2026: редактор, MCP и интеграции пользуются одним сервисом авторских данных.** Сам сервис умеет работать без окон; GUI добавляет выделение, gizmos и preview, MCP — типизированный доступ для агента, Blender — подготовку и обновление ассетов. Пользователь должен свободно переходить между этими способами, сохраняя историю, идентификаторы, проверки и результат. Это исследовательский проект контрактов от 17–18.09.2026. Реализованный MVP-профиль, точные имена tools и проверки описаны в [Manual MCP](../manual/editor/mcp.md), [Manual ресурсов](../manual/editor/assets.md) и [журнале реализации](../IMPLEMENTATION.md). Более широкие API-примеры ниже не являются обещанием текущей реализации. Актуальные решения — в [архитектуре](../ARCHITECTURE.md), этапы и критерии готовности — в [PLAN.md](../../PLAN.md). **MCP существует только в Editor/headless editor services: authoring, import, build, Play/Stop и журналы редактора. Никаких runtime inspection/mutation, world/session tools, MCP в Player, экспортной игре или SchemaExporter.** Он дополняет [UX редактора](./02-editor-ux.md), [разбор Godot](./08-godot-ux-source-study.md), [паттерны Blender](./10-blender-editor-patterns.md) и [ECS](./11-ecs-and-ergonomics.md). Исходники сверены в прежних commits: Godot `9c776068d6ed23acd0c78bfe534272d1d2a3a619`, Blender `28d47268bddcb9dc69143f0e2d9410969da16311`. Документация проверена 17.09.2026; для MCP зафиксирована редакция **2026-07-28**, поддержка которой конкретным клиентом не предполагается автоматически. ## 1. AuthoringService как самостоятельное ядро редактора Принятое разделение: UI / MCP adapter / importer вызывают AuthoringService; он владеет документами сцен, схемами компонентов, транзакциями, asset registry и job system. Runtime получает подготовленное представление через authoring→runtime bridge. Сервис может жить в процессе редактора или отдельном локальном процессе: важен единый API, а не обязательная микросервисная архитектура. Все вызовы содержат явные `project_id`, `document_id` и стабильные entity/asset IDs. Команда изменения не зависит от положения курсора, активного tab или текущего выделения. GUI разрешает selection в IDs перед вызовом; MCP сначала делает query и получает те же IDs. Имена остаются подписями; runtime handle с generation не используется как постоянный идентификатор документа. Практическое основание видно в Blender: одна операторская модель разделяет проверку контекста, интерактивный invoke и прямой exec, а завершение централизует undo. Для нашего headless API интерактивный сбор аргументов должен оставаться снаружи authoring-операции. [Диспетчер Blender](https://github.com/blender/blender/blob/28d47268bddcb9dc69143f0e2d9410969da16311/source/blender/windowmanager/intern/wm_event_system.cc#L1640), [завершение операции](https://github.com/blender/blender/blob/28d47268bddcb9dc69143f0e2d9410969da16311/source/blender/windowmanager/intern/wm_event_system.cc#L1279). ## 2. Типизированный API, который агент способен обнаружить Начальный набор семейств: `project.describe`, `schema.describe`, `scene.query`, `scene.inspect`, `edit.apply_batch`, `history.inspect`, `assets.import`, `build.start`, `operations.get/cancel`, `editor.play/stop`, `editor.logs`. Это **проектные имена tools**, не встроенные методы MCP. Все `scene.*` относятся к authoring-документу. Схемы описывают тип, единицы, диапазон, default, read-only, inherited/local provenance и правила ссылок; их декларативный источник общий с Inspector. `runtime.inspect/step`, чтение игровых компонентов и изменение симуляции не предоставляются. Preview может читать только зафиксированную authoring revision через editor renderer. `scene.query` принимает фильтр по типам компонентов, tags, asset references и bounds; возвращает выбранные поля, stable IDs, cursor и `snapshot_revision`. Продолжение pagination относится к тому же snapshot либо явно сообщает его истечение. `scene.inspect` отдаёт значение, источник, override и диагностику. Агенту не приходится угадывать JSON по screenshot или получать всю сцену для изменения одного поля. У команд и результатов есть версии схем, примеры аргументов и ограничения. Результат содержит `status`, `revision_before/after`, `transaction_id`, mapping временных IDs в созданные IDs, changed IDs, warnings и ссылки на подробный diff. Ошибки — структурированные `RevisionConflict`, `MissingTarget`, `ValidationFailed`, `UnsupportedCapability`, с точным полем и текущей revision. MCP поддерживает `inputSchema`, `outputSchema`, `structuredContent`, а ошибки выполнения tools отличаются от ошибок JSON-RPC. Сам MCP не обеспечивает наши транзакции или idempotency: это ответственность AuthoringService. [MCP Tools 2026-07-28](https://modelcontextprotocol.io/specification/2026-07-28/server/tools). Для расширения движка важен контракт плагина: C++ регистрация компонента экспортируется отдельным SchemaExporter в декларативный manifest; Inspector и editor MCP читают одну схему. Custom Inspector, исполняемые authoring validation/migration и commands относятся к отдельному editor module/helper-контракту. Gameplay-код не загружается в Editor ради обнаружения полей и не становится MCP tool автоматически. Произвольный `execute_python` или shell не является обязательным универсальным путём редактирования сцены. ## 3. Транзакции, одновременная работа человека и агента `edit.apply_batch` получает `base_revision`, `idempotency_key`, человекочитаемое название и список типизированных операций. Пример задачи: создать 20 фонарей, выставить transform, назначить общий prefab и индивидуальную мощность — один batch с временными ссылками между создаваемыми сущностями. Сервис сначала проверяет весь batch на изолированном состоянии, затем сравнивает revision и публикует результат целиком. Первая версия гарантирует атомарность **одного authoring document**; импорт ассета и правки нескольких сцен не объявляются общей атомарной транзакцией без отдельной реализации. Если человек успел изменить документ, сервер возвращает conflict и diff, не выполняя молчаливое last-write-wins. Для первого прототипа достаточно общей document revision; field-level conflict detection можно добавить позже. Idempotency scope включает проект и клиента; журнал хранит ключ, hash канонического payload и результат. Повтор того же запроса возвращает прежний результат; одинаковый ключ с иными аргументами — ошибка. Сохранение dedup record согласовано с commit, иначе сбой после создания объектов породит дубликаты при retry. Это ограниченная прикладная гарантия с документированным сроком хранения ключей, а не обещание «exactly once» любой внешней операции. **Ручной drag:** begin edit → много временных preview → один commit; Escape отменяет preview. **MCP:** один атомарный batch с готовыми значениями, без имитации mouse events. Оба пути создают одинаковый undo record и notifications. На время drag сервис может удерживать короткую lease на затронутые поля; конфликтующий batch получает `TargetBusy` либо revision conflict. Агент не должен незаметно двигать объект под рукой пользователя. Undo — отдельная authoring-функция с сохранёнными обратными данными и зависимостями. Для MCP `undo(transaction_id, expected_revision)` в v1 разрешён только для подходящей вершины истории; нельзя перескочить поверх поздней ручной правки и стереть её. Godot также явно связывает commit с историей документа, nested action и merge, а не просто выполняет обратный setter. [Godot commit_action](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/editor_undo_redo_manager.cpp#L246). ECS command buffer решает другую задачу: безопасно применить runtime structural changes в sync point. Он сам по себе не даёт persistent IDs, rollback, сохранение сцены или undo. В Player такие команды адресуют собственный runtime world и применяются в начале следующего fixed tick. Через MCP они недоступны; запуск/остановка Play не открывает канал чтения или записи игрового состояния. Автоматического переноса runtime-значений в authoring нет. ## 4. Долгие операции, отмена и протокольный адаптер Импорт, baking, сборка и сложный preview возвращают engine `operation_id`. `operations.get` показывает phase, выполненные work units, доступность cancel, diagnostics, artifact URIs и итоговую revision. Journal позволяет найти завершившуюся операцию после разрыва соединения. `operations.cancel` запрашивает кооперативную остановку; до точки публикации staging удаляется, после commit операция сообщает completed и undoability. Отмена запроса не равна откату уже сохранённой сцены. MCP adapter подбирает механизм под версию и capabilities клиента. Для живого запроса возможно protocol progress; для долгоживущих задач базовый переносимый путь — наши tools `operations.get/cancel`. В 2026-07-28 существует отдельное расширение Tasks с `tasks/get`, `tasks/update`, `tasks/cancel`; его можно отобразить на тот же job system при поддержке обеими сторонами. Tasks не используют обычные progress notifications, поэтому не нужно обещать их всем клиентам. [Progress](https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/progress), [Tasks extension](https://tasks.extensions.modelcontextprotocol.io/specification/2026-07-28/tasks). Transport cancellation тоже зависит от версии: в проверенной редакции HTTP SSE disconnect отменяет текущий request, stdio использует `notifications/cancelled`; durable task отменяется отдельно. Adapter должен сохранять семантику job, а не считать любую потерю HTTP-соединения подтверждённым rollback. [Cancellation 2026-07-28](https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/cancellation). Явные handles и собственные revisions полезны и для старых MCP-клиентов; перенос новых правил handshake на них не требуется. Для локального MVP подходит stdio bridge к выбранному проекту. MCP OAuth-профиль описывает HTTP; для stdio спецификация предусматривает получение credentials из окружения. Если позднее нужен сетевой HTTP endpoint, включается соответствующая authorization с проверкой доступа к проекту и операциям. Достаточно явных прав read/edit/build и видимого происхождения изменений в истории; окна подтверждения каждой обратимой правки не являются целью дизайна. [MCP Authorization](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization). ## 5. Наблюдаемость и возможности без GUI Headless editor умеет открыть проект, query/validate/edit/save authoring-сцены, импортировать ассеты, выполнять build и управлять процессом Play/Stop без чтения симуляции. Это не требует selection и оконного event loop. Возможности объявляются явно: `authoring`, `asset_import`, `build`, `editor_play_control`, `authoring_preview`; render capability зависит от доступного backend. Состояние job, compiler/import diagnostics и editor logs не подменяются runtime world query. Authoring screenshot — инструмент проверки редакторской правки, но не канал наблюдения за Player. Проектный `preview.render` принимает authoring scene revision, камеру, размер и overlays; результат связывает PNG с этими параметрами и render frame ID редакторского preview. При наличии renderer/GPU возможен offscreen preview без окна. На чистом CI без графического backend возвращается `UnsupportedCapability`, при этом редактирование данных остаётся доступным. Совпадение пикселей между любыми GPU не обещается. Diff, scene snapshots, dependency reports и логи доступны по engine resource URI; крупные артефакты выдаются ссылками, небольшое изображение можно вернуть image content. URI включает immutable revision или content hash, чтобы screenshot не выдавался за картинку более нового состояния. MCP действительно предусматривает ресурсы по URI, templates и binary contents; engine naming и retention policy задаём мы. [MCP Resources](https://modelcontextprotocol.io/specification/2026-07-28/server/resources). ## 6. Blender v1: glTF плюс manifest, исходники и overrides отдельно Принят обычный Blender без обязательного форка или MCP-плагина. Базовый путь — обычный экспорт GLB и импорт в Faset; importer хранит asset manifest/mapping. Необязательный собственный add-on упрощает экспорт выбранной collection, назначение устойчивых subasset IDs и согласованную публикацию GLB+manifest. Автор продолжает работать в `.blend`; runtime и обычный импорт движка его не открывают. Без source IDs нельзя гарантировать сохранение соответствия после произвольного rename/reparent; неоднозначность показывается пользователю. `bpy.ops.export_scene.gltf` официально имеет выбор format/collection, `export_extras`, `export_yup`, настройки материалов, skin/morph/animations и sampling. API предупреждает, что `export_apply` для modifiers препятствует экспорту shape keys. Поэтому export recipe фиксирует Blender/exporter version и все значимые опции, вместо зависимости от последнего состояния UI. [Blender export Python API](https://docs.blender.org/api/main/bpy.ops.export_scene.html). Manifest хранится вместе с authoring-данными в Git: `asset_id`, `bundle_revision`, source reference/hash, exporter/recipe version, dependencies, coordinate/unit policy, mapping object/mesh/material/animation IDs, engine metadata. Данные сначала пишутся в staging generation; проверяются hashes и completeness; затем один commit marker публикует поколение. Importer не должен видеть glTF нового поколения с manifest старого. Derived mesh/texture data и platform cache живут отдельно и пересобираются. Это согласуется с наблюдаемым подходом Godot: reimport читает настройки и UID из sidecar, вызывает importer с отдельным output path, затем записывает importer version и UID. [Сохранённые параметры](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/file_system/editor_file_system.cpp#L2826), [вызов и результат импорта](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/editor/file_system/editor_file_system.cpp#L2922). Предлагаемый staging/commit marker — наше усиление, не заявление об атомарности этих функций Godot. ## 7. Устойчивая идентичность при повторном импорте При использовании необязательного add-on первая публикация назначает UUID asset collection, объектам, mesh datablocks, материалам и поддерживаемым animation clips. Без add-on importer выдаёт собственные asset/subasset IDs и сохраняет mapping, но не выдаёт индекс или имя glTF за устойчивую source identity. IDs сохраняются в Blender custom properties и manifest; glTF `extras` можно использовать как дополнительный канал. В Godot прочитанный importer действительно переносит node extras в metadata, но это не означает автоматическую устойчивую идентичность: её семантику должен реализовать наш importer. [Node extras](https://github.com/godotengine/godot/blob/9c776068d6ed23acd0c78bfe534272d1d2a3a619/modules/gltf/gltf_document.cpp#L619). Имя и порядковый индекс glTF не являются ключом reimport. Duplicate создаёт новый object ID, сохраняя shared mesh ID, если геометрия разделяется. Helper обнаруживает случайно скопированные GUID и предлагает/выполняет однозначное исправление; linked assets получают namespace по source asset. Rename/reparent не меняют identity. Content hash служит для кэша, но не заменяет UUID: изменение вершины не должно превращать объект в новую сущность. Модель данных движка: immutable imported baseline + prefab/scene instances + локальные overrides по `(source_id, property_id)`. Reimport обновляет baseline, затем повторно накладывает overrides: placement экземпляра, выбранный engine material, gameplay components и collision settings сохраняются. Material slot требует собственного стабильного ключа, а не только позиции в массиве. Если исходный узел удалён, но имеет overrides/ссылки, возникает диагностируемый orphan/conflict; такие данные не следует молча выбрасывать. Границы владения обозначены в Inspector: геометрия и skeleton принадлежат Blender; gameplay и размещение instances — движку; engine-specific material replacement — локальному override. V1 roundtrip означает «открыть источник → изменить → экспортировать → безопасно обновить instances», а не безошибочную двустороннюю синхронизацию произвольных Blender-сцен. ## 8. Контракт формата и следующий шаг live link Профиль импорта должен последовательно покрыть triangle meshes, UV/нормали/tangents и metal-rough PBR; skinning, clips и morph targets вводятся на соответствующих этапах [плана](../../PLAN.md). Текущее проверенное статическое подмножество перечислено в [Manual ресурсов](../manual/editor/assets.md); skinning, clips и morph targets остаются за пределами MVP. Процедурные node graphs и Geometry Nodes не превращаются в engine shaders: нужен bake/evaluated mesh и отчёт о потерях. glTF описывает свою систему материалов, а Blender exporter распознаёт поддержанные узлы. [Blender: glTF materials](https://docs.blender.org/manual/en/4.0/addons/import_export/scene_gltf2.html). glTF использует правую систему координат, Y-up и метры; наши engine-конвенции фиксируются в recipe, с преобразованием ровно на одной границе. Проверять root transforms, nonuniform/negative scale, winding и tangent handedness. Имена glTF не гарантируют уникальность. [glTF 2.0: координаты и структуры](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html). Для animation заранее определить rest pose, sampling rate, root motion, clip ranges и ограничения joints/influences. Constraints/IK могут потребовать bake в поддержанные channels; не обещать перенос всей Blender-логики. Collision и gameplay не считать универсальной частью базового glTF: v1 описывает engine shapes в manifest либо строит их по явно маркированным meshes. Unsupported extensions выдаются списком до публикации; материалы не должны незаметно становиться «почти похожими». V2 live link использует типизированное локальное соединение helper↔AuthoringService: handshake версий, список capabilities, asset ID, base revision, generation/hash и операции `publish_asset`, `set_transform`, `focus_source`, `query_status`. Тяжёлая геометрия передаётся файлами/блоками; metadata — сообщениями. Revision/origin IDs подавляют echo loops; после reconnect сравниваются поколения. Непрерывный preview можно слать чаще, persistent commit — по подтверждению или завершению жеста. Обе стороны сохраняют собственный undo; согласованный commit получает correlation ID, но «одна общая undo-stack на два приложения» не обещается. ## 9. Три сквозных сценария приёмки **Ручная сцена → MCP → ручной Undo.** Пользователь ставит один фонарь, меняет цвет и сохраняет prefab. Агент находит prefab и поверхность query-запросом, одним batch размещает ряд экземпляров, возвращает IDs/diff/preview. Пользователь видит изменения в Tree/Inspector и одним Undo отменяет весь ряд. Повтор с тем же key/payload в пределах срока хранения dedup-журнала не создаёт дубликаты; конкурентный drag даёт понятный conflict; ручной и MCP-путь одинаково валидируют мощность света. **Blender → экземпляры → reimport.** Художник публикует дверь; пользователь ставит три экземпляра, одному меняет материал, всем добавляет gameplay. В Blender дверь переименовывается, меняется mesh и animation. Повторный экспорт сохраняет IDs, placement и overrides; удаление узла с override создаёт conflict. Прерванный экспорт оставляет последнее корректное поколение. После очистки derived cache импорт повторяется с теми же semantic IDs и содержимым в пределах заданного recipe. **Headless editor MCP → build → возвращение в GUI.** Агент без окна редактора импортирует bundle, правит и валидирует authoring-сцену, выполняет build, получает compiler/import/editor diagnostics. Play/Stop управляет только жизнью процесса; при поддержке renderer доступен PNG authoring revision, не runtime capture. Cancel до commit не публикует частичный результат; после reconnect операция находится по ID. GUI открывает тот же документ с той же revision и историей. Проверка отклоняет любые запросы MCP на чтение или изменение runtime; Player и SchemaExporter не содержат MCP-зависимостей. ## 10. Порядок разработки Порядок до MVP и после него зафиксирован в [PLAN.md](../../PLAN.md). Авторский vertical slice: сцена с компонентами, headless AuthoringService, собственный retained Inspector и MCP поверх экспортированной схемы, atomic batch/revision/idempotency, один undo. Затем glTF+manifest importer и повторный импорт с overrides. Затем async jobs, диагностика и offscreen preview. Только после проверенного roundtrip — Blender live link. Измерять время от намерения до видимого результата, число вызовов/действий, объём ответа query, latency commit/undo, ошибки конфликтов и сохранность overrides. Критерий успеха — одна и та же задача удобно выполняется руками, агентом и с внешним asset source, а переход между ними не требует чинить скрытое состояние.