Files
kompas3d-mcp/docs/ARCHITECTURE.md
T
mikhail 3a98afc67f chore: initial skeleton + KOMPAS connection spike (S1/S2)
Зафиксирован фундамент проекта MCP-сервера КОМПАС-3D:

- docs/: ARCHITECTURE.md, IMPLEMENTATION_PLAN.md, presentation.html
- tools/kdoc.py: навигатор по справке SDK
- libs/kompas-interop/: вендорские interop-сборки КОМПАС из SDK
- src/Kompas.Mcp.ConnectSpike: net8.0-windows консоль, доказывает
  работу COM-интеропа и attach/launch подключения к КОМПАС
  (IApplication, версия 24.0)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 23:28:29 +03:00

306 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Архитектура MCP-сервера КОМПАС-3D (предлагаемый вариант)
> Статус: **черновик / предлагаемый вариант**, открыт к правкам.
> Назначение: зафиксировать выбор стека и целевую архитектуру решения до начала
> реализации. Сопутствующий контекст по COM API КОМПАС — в [`../CLAUDE.md`](../CLAUDE.md).
> Имена интерфейсов и перечислений сверены со справкой SDK через `tools/kdoc.py`.
---
## 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<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. Слои решения
В v1 — логические слои (папки/проекты одного solution `KompasMcp.sln`):
| Слой | Ответственность |
|---|---|
| **Kompas.Mcp.Host** | Точка входа. Сборка MCP-сервера (stdio), DI, логи в stderr, старт и владение STA-потоком, graceful shutdown. |
| **Kompas.Mcp.Tools** | Определения MCP-инструментов по категориям (System / Documents / Sketch / Features / Query). **Тонкий слой**: валидация аргументов → постановка задачи на STA-поток → форматирование результата/ошибки в MCP-ответ. |
| **Kompas.Mcp.Core** (`KompasSession`) | COM-слой. Менеджер соединения, менеджер документов, построитель эскизов, построитель операций, помощники выборки граней/плоскостей. Безопасные типизированные методы, null-проверки, трансляция ошибок. Здесь живут все паттерны работы с API5/API7. |
| **Kompas.Mcp.Interop** | Interop-сборки из `.tlb` + маппинг «дружелюбная строка ↔ перечисление» (`DocumentTypeEnum`, `Obj3dType`, `ksEndTypeEnum`, `ksDirectionTypeEnum`). |
Принцип: **только `Core` и `Interop` знают про COM**. `Tools` оперируют доменными
DTO и вызывают `Core`; `Host` не содержит бизнес-логики.
---
## 5. Карта инструментов v1
Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — `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` — перестроить деталь.
**Selection / Query**
- `select_face_by_point` — выбрать грань точкой (для следующего эскиза).
- `list_bodies`, `list_faces_planes` — перечислить тела/грани и плоскости.
- `get_part_info` — МЦХ/габариты (`IMassInertiaParam7`).
**Vision — визуальная обратная связь («зрение агента»)**
- `model_snapshot` — отрендерить активный 3D-документ в PNG и вернуть **как image-контент MCP**,
чтобы мультимодальный агент *увидел* промежуточный результат и контролировал построение пошагово.
- `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)
1. Операции: `fillet` / `chamfer` / `shell` / `rib` / `loft` / `sweep`; массивы
(`ILinearPattern`, `ICircularPattern`).
2. Параметры и свойства: `IVariable7` / `IVariableTable`, `IPropertyMng` / `IPropertyKeeper`.
3. Полноценное 2D-черчение: виды, линии/дуги/окружности, размеры (`ILinearDimension`, …),
штриховки, тексты.
4. Спецификации (`ISpecification`), сборки и сопряжения (`IAssemblyDocument`).
5. Конвертеры/экспорт (STEP и др.) через `IApplication.Converter`.
6. Транспорт **HTTP/SSE** — как опция для удалённых клиентов.
---
## 11. Открытые вопросы
- **Один проект vs solution из 4 проектов** в v1 (склоняемся к одному solution, слои — папками,
выделять проекты по мере роста).
- **Embed Interop Types vs генерируемые interop-сборки** (`tlbimp`) — проверить на реальных
`.tlb` v24, выбрать по совместимости типов и размеру.
- **Стратегия именования инструментов** — подтвердить `snake_case` с группами-префиксами
(`document_*`, `sketch_*`, …).