Files
kompas3d-mcp/docs/ARCHITECTURE.md
T
mikhail 28d2ae3868 feat(inspection): структурное «зрение» модели — осмотр без снимка
Новый слой «модель как данные» (приоритетный над model_snapshot):
- describe_model — структурный «паспорт» детали одним вызовом (габарит,
  МЦХ, тела, сводка топологии, дерево построения с параметрами, переменные)
- list_features / list_bodies / list_variables
- describe_face / describe_edge (drill-down по индексу)
- measure (расстояние/угол между гранями/рёбрами/вершинами)
- model_snapshot понижен до fallback для визуально-пространственных вопросов

Реализация: ModelInspectionService (COM) + чистый InspectionText (рендер,
группировка, детект «импорт без истории») + record-модели; InspectionTools.
Дерево читается через ksPart.GetFeature()->SubFeatureCollection; точный тип
и параметры операций берутся из GetObject()->GetDefinition() (ksFeature.type
отдаёт лишь coarse o3d_entity).

Снимок дерева также включает слои conversion (STEP), editing (move_face/solid)
и validation, развивавшиеся параллельно. Полный прогон: 61 тест зелёный,
43 MCP-инструмента. Документация (README, ARCHITECTURE, OPEN_QUESTIONS,
CLAUDE.md, presentation.html) синхронизирована.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 18:44:38 +03:00

26 KiB
Raw Blame History

Архитектура MCP-сервера КОМПАС-3D (предлагаемый вариант)

Статус: реализовано и работает (v1+v2+STEP/assembly+direct-edit+inspection). Актуализировано по коду. Сопутствующий контекст по COM API КОМПАС — в ../CLAUDE.md. Имена интерфейсов и перечислений сверены со справкой SDK через .claude/skills/kompas-sdk-research/kdoc.py (навык kompas-sdk-research).


1. Назначение и контекст

Строим MCP-сервер, который даёт LLM управлять CAD-системой КОМПАС-3D (АСКОН): создавать/открывать документы, строить 2D-геометрию, 3D-детали и сборки, читать/писать параметры, запускать команды.

Жёсткие рамки, заданные природой API:

  • Только Windows, только x64 — КОМПАС автоматизируется через COM Automation API; разрядность сервера должна совпадать с разрядностью установленного КОМПАС.
  • КОМПАС должен быть установлен и запускаем на той же машине. Сервер — это COM-клиент, а не самостоятельный геометрический движок. Без работающего экземпляра КОМПАС инструменты бесполезны.
  • Целевая среда — КОМПАС-3D v24 Home (LT-редакция); часть возможностей полной редакции может быть недоступна.

Две сосуществующие версии API (подробнее — CLAUDE.md):

Точка входа Namespace (C#) Когда
API5 (legacy) KompasObject Kompas6API5 bootstrap-подключение, построение 3D через ksPart
API7 (современный) IApplication KompasAPI7 всё новое: документы, параметры, операции

2. Выбранный стек

Решение Выбор Обоснование
Язык / платформа .NET 8 (LTS), C#, win-x64 COM-интероп первого класса; SDK поставляет именно C#-примеры и .tlb
MCP SDK ModelContextProtocol (официальный C# SDK) хостинг через Microsoft.Extensions.Hosting, DI, готовый stdio-транспорт
Транспорт stdio локальный клиент (Claude Desktop / IDE) запускает сервер дочерним процессом на машине с КОМПАС — проще и безопаснее всего
COM-интероп COM-references на .tlb из SDK\lib строгая типизация интерфейсов API7 (IApplication, IPart7, …) на этапе компиляции
Логи Microsoft.Extensions.Loggingstderr stdout занят протоколом MCP — туда писать нельзя
Сериализация System.Text.Json штатно в .NET, без зависимостей

Подключаемые типобиблиотеки (C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK\lib): kAPI5.tlb, kAPI7.tlb, ksConstants.tlb, ksConstants3D.tlb, kAPI2D5COM.tlb, kAPI3D5COM.tlb. Namespaces: Kompas6API5, KompasAPI7, Kompas6Constants, Kompas6Constants3D, KAPITypes.

Почему C#, а не Python

Python (comtypes/pywin32) технически способен управлять COM, но КОМПАС-API активно использует конструкции, болезненные для динамического интеропа: out/ref-параметры (например, ExecuteKompasCommand(commandID, post, &retval)), SAFEARRAY/динамические массивы (ksDynamicArray, ksLtVariant), мосты API5↔API7 (ksGetApplication7, TransferInterface) и обязательный STA + насос сообщений. В C# всё это покрыто строго типизированным interop, что снижает риск и стоимость поддержки.


3. Модель процесса и потоков (ключевой раздел)

COM-объекты КОМПАС живут в STA (Single-Threaded Apartment), экземпляр приложения — единственный и однопоточный. Отсюда — центральное архитектурное ограничение:

  • Сервер держит один выделенный STA-поток, который владеет всеми без исключения COM-вызовами. Асинхронные обработчики MCP-инструментов (поток-пул) маршалят работу в этот поток через очередь/диспетчер (например, BlockingCollection<Func<…>> или собственный SynchronizationContext). Это автоматически сериализует доступ к КОМПАС.
  • STA-поток крутит насос сообщений Windows и периодически вызывает IApplication.PumpWaitingMessages() / при необходимости EnableTaskAccess(false) на время пакетных операций — чтобы диалоги и колбэки КОМПАС не приводили к взаимоблокировке.
MCP client ──stdio──> Host (поток-пул, async)
                         │  ставит задачу в очередь
                         ▼
                 ┌─────────────────────────┐
                 │  STA-поток (COM owner)   │  ← насос сообщений + PumpWaitingMessages
                 │  KompasObject / IApplication
                 └─────────────────────────┘
                         │ COM
                         ▼
                    КОМПАС-3D.exe

Подключение к КОМПАС

  1. Разрешить ProgID. На целевой машине (v24 Home) проверено: зарегистрированы KOMPAS.Application.7 и KOMPAS.Application.5, а KOMPASLT.* отсутствует — то есть Home-редакция использует обычные ProgID, а не LT. Порядок попыток: KOMPAS.Application.7 (прямой API7) → KOMPAS.Application.5 (+ ksGetApplication7()) → KOMPASLT.Application.5 (запасной, для других инсталляций). Выбранный ProgID фиксировать в логе.
  2. Attach к запущенному экземпляру: Marshal.GetActiveObject(progId). Если не запущен — launch: Activator.CreateInstance(Type.GetTypeFromProgID(progId)).
  3. Если зашли через API5 — получить современное приложение: IApplication appl = (IApplication)kompas.ksGetApplication7(); (через KOMPAS.Application.7 IApplication уже на руках).
  4. appl.Visible = true; — показать окно КОМПАС; дальше работать через интерфейсы API7.

Жизненный цикл COM

  • Корневые RCW (KompasObject, IApplication) держим на всё время жизни сервера.
  • Временные объекты освобождаем (Marshal.ReleaseComObject) в обратном порядке создания.
  • appl.Quit() вызываем только если экземпляр запустили мы сами (флаг «launched-by-us»); к чужому запущенному КОМПАС не применяем.

4. Слои решения

Проекты solution KompasMcp.slnx (спайк-проекты ConnectSpike/ReconSpike удалены; их механика покрыта тестами):

Проект Ответственность
Kompas.Mcp.Host Точка входа. Сборка MCP-сервера (stdio), DI (в т.ч. ConversionService), логи в stderr, старт и владение STA-потоком, graceful shutdown. Определения инструментов по категориям (System / Documents / Sketch / Features / Query / Conversion). Тонкий слой: валидация аргументов → постановка задачи на STA-поток → форматирование результата/ошибки в MCP-ответ.
Kompas.Mcp.Core COM-слой. Менеджер соединения, менеджер документов, построитель эскизов и операций, ConversionService (STEP импорт/экспорт), QueryService (МЦХ, грани, рёбра, компоненты), ModelInspectionService (дерево операций, тела, переменные, drill-down, измерения), снимок. Все паттерны API5/API7 живут здесь.
Kompas.Mcp.Tests Юнит (без COM) + интеграционные (требуют КОМПАС). 60 тестов.

Принцип: только Core знает про COM. Host/Tools оперируют доменными DTO и вызывают Core; Host не содержит бизнес-логики.

Архитектурный принцип: MCP транслирует возможности SDK КОМПАС в общие инструменты (не под конкретную задачу). Методический слой поверх MCP — навык .claude/skills/kompas-3d/ (playbook'и, эвристики). Полигон отработки подходов — usecases/ (в .gitignore).


5. Карта инструментов (43 инструмента)

Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — snake_case.

System

  • kompas_connect — attach/launch, возвращает версию и редакцию (GetSystemVersion, ApplicationName).
  • kompas_set_visible — показать/скрыть окно (IApplication.Visible).
  • kompas_status — состояние соединения, активный документ.

Documents (IApplication.DocumentsIDocuments)

  • document_create — тип part | assembly | drawing | fragment (DocumentTypeEnum: Part=4, Assembly=5, Drawing=1, Fragment=2).
  • document_open / document_save / document_close / document_active.

Sketch (2D-геометрия внутри 3D-эскиза)

  • sketch_create — на базовой плоскости (o3d_planeXOY / XOZ / YOZ) или на выбранной грани.
  • sketch_add_line / sketch_add_circle / sketch_add_arc / sketch_add_rectangle.
  • sketch_close — завершить редактирование.
  • Жизненный цикл: NewEntity(o3d_sketch)SetPlane(plane|face)BeginEdit()ksLineSeg / ksCircle / … → EndEdit().

Features (3D-операции)

  • extrude_boss / extrude_cut — sketch, depth, direction, endType.
  • revolve_boss / revolve_cut — угол.
  • rebuild — перестроить деталь.

Edit (прямое редактирование)

  • move_face — сдвинуть грань по мировой точке (x,y,z, мм) на distance мм вдоль нормали; distance>0 — наружу (добавить материал), <0 — внутрь. Работает на импортированной B-rep. Реализовано через API7 IPart7.FindObjectsByPoint + FaceMover; контейнеры ISurfaceContainer/IModelContainer получаются COM-QI от IPart7 в рантайме. Проверено: top_spacer 39.45→41.45 мм.

Selection / Query

  • get_part_info — МЦХ через API5 ksPart.CalcMassInertiaProperties(ST_MIX_MM|ST_MIX_KG).
  • get_bounding_box — габарит (ksPart.GetGabarit).
  • list_faces / list_edges — перечислить грани и рёбра детали с классификацией и мерами.
  • list_components — перечислить компоненты верхнего уровня сборки (индекс, имя, обозначение, признак детали, габарит) через TopPart → IParts7.

Conversion (ConversionTools.cs, ConversionService.cs)

  • import_step — импорт .step/.stp в новый документ через встроенный конвертер КОМПАС (код ksConverterFromSTEP=-3); параметры type (assembly|part), createComponentFiles.
  • export_step — экспорт активного 3D-документа в STEP; format = auto|ap203|ap214|ap242.

Inspection — структурный осмотр модели (приоритетный над снимком)

  • describe_model — структурный «паспорт» детали одним вызовом: габарит, МЦХ, тела, сводка топологии (грани/рёбра по типам), дерево построения с параметрами, переменные. Предпочитать перед model_snapshot — не тратит токены изображения.
  • list_features — дерево построения с параметрами (глубина/радиус/катеты), имена узлов локализованы КОМПАС.
  • list_bodies — тела детали: тип (твёрдое/поверхность) и число граней.
  • list_variables — переменные модели (имя/выражение/значение, флаги external/информационная).
  • describe_face — drill-down грани по индексу: тип, площадь, нормаль, радиус, число рёбер.
  • describe_edge — drill-down ребра по индексу: тип, длина, смежные грани, концевые вершины.
  • measure — расстояние и угол между двумя объектами (face|edge|vertex по индексам), единица ST_MIX_MM.

Реализовано через ModelInspectionService (src/Kompas.Mcp.Core/Query/ModelInspectionService.cs), рендер текста — InspectionText (InspectionText.cs, юнит-тестируем без COM). Ключевые API-паттерны: ksPart.GetFeature()ksFeatureCollection (дерево); BodyCollection()ksBody; VariableCollection()ksVariable; GetMeasurer()ksMeasurer. «Импорт STEP без истории» определяется структурно: bodyCount > 0 && нет формообразующих операций && features.Count <= bodyCount+1.

Vision — визуальная обратная связь (fallback)

  • model_snapshot — отрендерить активный 3D-документ в PNG и вернуть как image-контент MCP, чтобы мультимодальный агент увидел промежуточный результат. Использовать для визуально-пространственных вопросов, когда структурный осмотр (describe_model) недостаточен.
  • set_view — задать ориентацию вида (iso / front / top / right / …), zoom-to-fit и режим отображения (ShadedWireframe) перед снимком — иначе снимок берётся со случайного текущего ракурса.

Механизм. ksDocument3D.RasterFormatParam()IRasterFormatParam рендерит документ в растр; свойство resultArrayBytes возвращает изображение как массив байт в память (без временного файла). Альтернатива — экспортные функции ksSaveAsToRasterFormat[W]. Полученные байты сервер упаковывает в image-контент MCP-ответа (C# SDK ModelContextProtocol это поддерживает). Параметры: формат (PNG/JPEG/BMP/TIFF), разрешение, цвет/greyScale. Снимки делать после значимых операций (не после каждой линии) — каждый кадр расходует токены изображения в контексте агента. На v24 Home качество/доступность рендера проверить на прототипе.

Escape hatch (опционально)

  • run_kompas_commandIApplication.ExecuteKompasCommand(commandID, post) для команд, ещё не обёрнутых в типизированные инструменты. Доступность проверять через IsKompasCommandEnable.

Замечание об API5 vs API7 для 3D-операций

API7 создаёт операции через IPart7 → (QueryInterface) IModelContainerIExtrusions.Add()IExtrusion. Примеры SDK при этом активно используют API5-путь (ksPart.NewEntity(o3d_…)ksBossExtrusionDefinition.SetSideParam/SetSketchCreate()), который хорошо документирован. Стратегия v1: предпочитать API7 там, где он покрывает операцию; падать на API5 ksPart-путь там, где он проще/надёжнее (мост — kompas.TransferInterface). Выбор фиксировать в Core.


6. Центральный сценарий: цикл «эскиз → операция → эскиз»

Основной пользовательский поток v1 — итеративное параметрическое моделирование.

document_create(part)
        │
        ▼
  sketch_create(plane = XOY) ──► sketch_add_circle(...) ──► sketch_close
        │
        ▼
  extrude_boss(sketch, depth=…, dir=dtNormal, end=etBlind)   → цилиндр
        │
        ▼
  set_view(iso, fit) → model_snapshot   👁 агент СМОТРИТ на результат и решает следующий шаг
        │
        ▼
  select_face_by_point(x,y,z)        ← выбираем верхнюю грань
        │
        ▼
  sketch_create(face = выбранная)  ──► sketch_add_circle(...) ──► sketch_close
        │
        ▼
  extrude_cut(sketch, depth=…)        → отверстие
        │
        ▼
  model_snapshot                     👁 проверка результата
        │
        └──────────► повторять: смотрим → новая грань → новый эскиз → новая операция

Соответствие API (на STA-потоке, через Core):

  1. Document3D() / IKompasDocument3DIPart7 (вершинная деталь, pTop_Part).
  2. part.NewEntity(o3d_sketch)GetDefinition()SetPlane(базовая_плоскость | грань)Create(); BeginEdit() → рисование примитивов (ksLineSeg, ksCircle, ksArcByPoint, …) → EndEdit().
  3. part.NewEntity(o3d_bossExtrusion)ksBossExtrusionDefinitionSetSideParam(direction, ksEndTypeEnum, depth, …)SetSketch(эскиз)Create(). Для выреза — o3d_cutExtrusion; для вращения — o3d_bossRotated/o3d_cutRotated.
  4. Выбор грани для следующего эскиза: part.EntityCollection(o3d_face)SelectByPoint(x, y, z) → элемент коллекции как плоскость эскиза.
  5. Снимок результата: set_view (ориентация + fit) → ksDocument3D.RasterFormatParam()IRasterFormatParam.resultArrayBytes → image-контент MCP. Агент анализирует кадр и выбирает следующий шаг.

Используемые перечисления: ksDirectionTypeEnum (dtNormal/dtReverse/dtBoth/dtMiddlePlane), ksEndTypeEnum (etBlind/etThroughAll/etUpTo…), Obj3dType (o3d_sketch, o3d_bossExtrusion, o3d_cutExtrusion, o3d_face, o3d_planeXOY, …).


7. Обработка ошибок и безопасность

  • Трансляция ошибок COM в структурированные MCP-ошибки: проверять HRESULT каждого вызова и опрашивать kompas.ksGetLastError() / IApplication.KompasError; возвращать клиенту код + текст.
  • Null-чек после каждого приведения COM-интерфейса (любой каст может вернуть null).
  • .Init() на param-структурах перед использованием (GetParamStruct(...).Init()).
  • Гарантированный EndEdit для эскизов через try/finally — незакрытый эскиз ломает документ.
  • Подтверждение деструктивных операций: закрытие без сохранения, перезапись файла, Quit — только по явному флагу в аргументах инструмента.
  • Модель состояния: инструменты по умолчанию работают с «активным документом»; опционально — явный хэндл/идентификатор документа.

8. Сборка и запуск

dotnet build -c Release -r win-x64        # сборка
# → kompas-mcp.exe (self-contained, win-x64)

Регистрация stdio-сервера у клиента (пример для конфигурации Claude Desktop):

{
  "mcpServers": {
    "kompas3d": { "command": "C:\\path\\to\\kompas-mcp.exe", "args": [] }
  }
}

Ручная проверка (smoke): запустить КОМПАС (или дать серверу запустить его) → через инструменты построить простую деталь (выдавить окружность в цилиндр, затем extrude_cut — отверстие) → убедиться визуально в окне КОМПАС → сохранить .m3d.


9. Стратегия тестирования

  • Интеграционные тесты (требуют установленного КОМПАС): xUnit, категория Integration, гейтятся (не идут в обычном CI без КОМПАС). Покрывают связку Core↔COM и центральный цикл.
  • Unit-тесты чистой логики без COM: маппинг строк↔перечислений, валидация аргументов, форматирование результатов/ошибок.
  • Smoke MCP: проверка протокола через MCP Inspector или тестовый клиент (list tools, вызов kompas_connect/document_create).

10. Дорожная карта

Реализовано (v1+v2+STEP/assembly+direct-edit+inspection): документы, эскизы, выдавливание/вырез, вращение, скругление/фаска, снимок; get_part_info, get_bounding_box, list_faces, list_edges; import_step, export_step, list_components; move_face (прямое редактирование грани, работает на импортированной B-rep); describe_model, list_features, list_bodies, list_variables, describe_face, describe_edge, measure (структурный осмотр модели).

Следующие приоритеты:

  1. Рассечение/перемещение тела как MCP-инструменты (SplitSolids/BodyRepositions) — механика есть, продуктизация не закончена.
  2. Массивы (ILinearPattern, ICircularPattern).
  3. Свойства документа: IPropertyMng / IPropertyKeeper.
  4. Полноценное 2D-черчение: виды, линии/дуги/окружности, размеры (ILinearDimension, …), штриховки, тексты.
  5. Спецификации (ISpecification), построение сборок и сопряжения.
  6. Транспорт HTTP/SSE — как опция для удалённых клиентов.

11. Открытые вопросы

  • Прямое редактирование B-rep, перемещение грани: решено — move_face (FaceEditService) работает через IPart7.FindObjectsByPoint + FaceMover; проверено на импортированной B-rep. Рассечение/перемещение тела (SplitSolids/BodyRepositions) — механика есть, инструменты не реализованы (потенциальная следующая опция).
  • Дерево операций, тела, переменные, геометрические запросы: решено — ModelInspectionService + InspectionTools (7 инструментов); юнит-тестируемый рендер через InspectionText.
  • Утечки транзитных RCW в PartModeler (унаследованный паттерн): EntityCollection, GetTopPart() не освобождаются, накапливаются за длинную сессию.
  • Насос сообщений / CancellationToken: CancellationToken не прерывает идущий COM-вызов; зависший модальный диалог КОМПАС блокирует всю очередь.
  • Embed Interop Types vs вендорские interop-сборки: выбраны вендорские DLL из SDK Samples/Common; менять не планируется.