# Архитектура MCP-сервера КОМПАС-3D (предлагаемый вариант) > Статус: **реализовано и работает** (v1+v2+STEP/assembly+direct-edit+inspection). Актуализировано по коду. > Сопутствующий контекст по COM API КОМПАС — в [`../CLAUDE.md`](../CLAUDE.md). > Имена интерфейсов и перечислений сверены со справкой SDK по MD-базе знаний `docs/Kompas3D_SDK/` (навык `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.Logging` → **stderr** | **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>` или собственный `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.Documents` → `IDocuments`) - `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_command` — `IApplication.ExecuteKompasCommand(commandID, post)` для команд, ещё не обёрнутых в типизированные инструменты. Доступность проверять через `IsKompasCommandEnable`. ### Замечание об API5 vs API7 для 3D-операций API7 создаёт операции через `IPart7` → (QueryInterface) `IModelContainer` → `IExtrusions.Add()` → `IExtrusion`. Примеры SDK при этом активно используют **API5-путь** (`ksPart.NewEntity(o3d_…)` → `ksBossExtrusionDefinition.SetSideParam/SetSketch` → `Create()`), который хорошо документирован. **Стратегия 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()` / `IKompasDocument3D` → `IPart7` (вершинная деталь, `pTop_Part`). 2. `part.NewEntity(o3d_sketch)` → `GetDefinition()` → `SetPlane(базовая_плоскость | грань)` → `Create()`; `BeginEdit()` → рисование примитивов (`ksLineSeg`, `ksCircle`, `ksArcByPoint`, …) → `EndEdit()`. 3. `part.NewEntity(o3d_bossExtrusion)` → `ksBossExtrusionDefinition` → `SetSideParam(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. Сборка и запуск ```powershell dotnet build -c Release -r win-x64 # сборка # → kompas-mcp.exe (self-contained, win-x64) ``` Регистрация stdio-сервера у клиента (пример для конфигурации Claude Desktop): ```json { "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`; менять не планируется.