Документация переписана под состояние без каталога docs/
Каталог docs/ удалён в b022a91, но CLAUDE.md и README ссылались на
ARCHITECTURE.md, TODO.md, OPEN_QUESTIONS.md, IMPLEMENTATION_PLAN.md и
presentation.html — все ссылки были битыми.
- CLAUDE.md: явно зафиксировано, что прозаическая документация — только он и
README, docs/ воссоздавать не нужно; бэклог (чертёж: базы/допуски/привязка,
сопряжения сверх coincidence и distance) и известный подводный камень с
направлением по нормали грани перенесены из удалённых TODO/OPEN_QUESTIONS
- убран субагент docs-maintainer: он удалён вместе с docs/, доки правятся руками
- ссылка на спеку по параметрическим эскизам заменена на память
- README: структура репозитория приведена к реальной, добавлены --version,
run-ps-tests.ps1 и оговорка про .slnx на SDK 8
- kompas-mcp-dev: справка SDK описана как RAG-база kompas-sdk вместо
несуществующего docs/Kompas3D_SDK/, добавлен порядок работ по новому
инструменту, команды проверки и границы CI
Счётчики сверены по коду: 84 инструмента, 250 unit + 131 integration, 41 Pester.
@
This commit is contained in:
@@ -1,22 +1,22 @@
|
||||
---
|
||||
name: kompas-mcp-dev
|
||||
description: Внутренняя методика доработки самого MCP-сервера КОМПАС-3D в этом репозитории — полигон usecases/, продуктизация приёма в общий инструмент, субагенты kompas-sdk-research и docs-maintainer. Используй, когда задача — доработать сервер, а не построить деталь. Для построения деталей — навык kompas-3d.
|
||||
description: Внутренняя методика доработки самого MCP-сервера КОМПАС-3D в этом репозитории — два слоя (инструмент/методика), полигон usecases/, продуктизация приёма в общий инструмент, делегирование поиска по справке SDK субагенту kompas-sdk-research. Используй, когда задача — доработать сервер (новый инструмент, тесты, плагин, CI), а не построить деталь. Для построения деталей — навык kompas-3d.
|
||||
---
|
||||
|
||||
# kompas-mcp-dev — доработка MCP-сервера КОМПАС-3D
|
||||
|
||||
Заготовка: сюда перенесён материал из навыка `kompas-3d`, относящийся к разработке сервера,
|
||||
а не к построению деталей. Доводка — вместе с проработкой рабочего цикла репозитория.
|
||||
Область: код сервера, тесты, упаковка в плагин. Построение деталей — навык `kompas-3d`.
|
||||
Архитектура, проверенные COM-цепочки и подводные камни — в `CLAUDE.md` (единственный
|
||||
архитектурный документ: каталога `docs/` в репозитории нет и заводить его не нужно).
|
||||
|
||||
## Два слоя (важно не смешивать)
|
||||
|
||||
- **MCP-сервер** (`src/Kompas.Mcp.Host`) — *инструмент*. Он **транслирует возможности COM API/SDK
|
||||
КОМПАС** в MCP-инструменты. Инструменты делаются **общими** (по возможностям SDK), а не под
|
||||
конкретную задачу. Расширяя MCP, добавляй универсальную операцию (например `import_step`,
|
||||
`list_components`, `export_step`, «переместить грань», «рассечь тело»), а не «сделать вот эту деталь».
|
||||
конкретную задачу. Расширяя MCP, добавляй универсальную операцию (`import_step`, `list_components`,
|
||||
«переместить грань», «рассечь тело»), а не «сделать вот эту деталь».
|
||||
- **Навык `kompas-3d`** — *методика*. Как из общих инструментов собрать решение: порядок шагов,
|
||||
выбор геометрии, проверка результата, обход подводных камней. Подходы вырабатываются в
|
||||
**`usecases/`** и поднимаются туда.
|
||||
выбор геометрии, проверка результата, обход подводных камней.
|
||||
|
||||
Итог: **MCP = чем делать, навык = как делать.** Новый приём сначала обкатывается кейсом, потом
|
||||
обобщается: инструмент → в MCP, метод → в навык `kompas-3d`.
|
||||
@@ -24,19 +24,54 @@ description: Внутренняя методика доработки самог
|
||||
## Продуктизация приёма
|
||||
|
||||
> Если нужного инструмента ещё нет — не хардкодь обход под кейс. Заведи кейс в `usecases/`,
|
||||
> докажи механику (тест или минимальный прогон в usecases/), затем продуктизируй как
|
||||
> **общий** инструмент MCP (с тестами, TDD) и опиши приём в навыке `kompas-3d`.
|
||||
> докажи механику (спайк или минимальный прогон), затем продуктизируй как **общий** инструмент MCP
|
||||
> (с тестами, TDD) и опиши приём в навыке `kompas-3d`.
|
||||
|
||||
Порядок для нового инструмента:
|
||||
|
||||
1. **Сигнатуры и константы** — через субагента `kompas-sdk-research` (см. ниже), затем перепроверка
|
||||
рефлексией по `libs/kompas-interop/*.dll`: справка врёт (напр. `IRadialDimension.NominalValue`
|
||||
отдаёт радиус, а не диаметр; `direction` у `ksInclineDefinition` инвертирован).
|
||||
2. **Спайк вживую** — минимальный прогон на запущенном КОМПАС; COM-цепочка считается верной только
|
||||
после этого.
|
||||
3. **Сервис в `src/Kompas.Mcp.Core/<домен>/`** + валидация параметров отдельным статическим классом
|
||||
(`*Validation`, `SketchGeometry`) — она покрывается unit-тестами без COM.
|
||||
4. **Инструмент в `src/Kompas.Mcp.Host/Tools/<Группа>Tools.cs`** — по файлу на группу.
|
||||
5. **Тесты**: unit на валидацию/маппинг перечислений, integration на реальную геометрию
|
||||
(класс наследует `IntegrationTestBase`, артефакты — в `.scratch/`).
|
||||
6. **Документация**: каталог инструментов в `README.md` (+ счётчик в заголовке), проверенная
|
||||
COM-цепочка и подводные камни — в `CLAUDE.md` §«Key implementation facts» и в память
|
||||
(`kompas-*-api*.md`). Правь эти файлы сам — субагента для документации больше нет.
|
||||
|
||||
## Проверка
|
||||
|
||||
```powershell
|
||||
dotnet build -c Release
|
||||
dotnet test --filter "Category=Unit" # без COM
|
||||
dotnet test --filter "Category=Integration" # нужен запущенный КОМПАС
|
||||
pwsh -NoProfile -File tools/tests/run-ps-tests.ps1 # PowerShell плагина (Pester)
|
||||
```
|
||||
|
||||
CI гоняет только `Category=Unit&Requires!=Windows` на Linux-раннере — integration там не запустится
|
||||
никогда. Формат решения `.slnx` понимает лишь SDK 9+, поэтому в CI проекты указаны путями.
|
||||
|
||||
## Делегирование
|
||||
|
||||
- **Поиск по справке SDK** (сигнатуры, константы, перечисления, цепочки COM-вызовов) — субагент
|
||||
**`kompas-sdk-research`** (Haiku, read-only, по MD-базе `docs/Kompas3D_SDK/`). Не грепай справку сам.
|
||||
- **Правка документации проекта** (`README.md`, `CLAUDE.md`, `docs/*`) — субагент **`docs-maintainer`**
|
||||
(Sonnet). Передавай ему сводку изменений, не правь доки вручную.
|
||||
**Поиск по справке SDK** (сигнатуры, константы, перечисления, цепочки COM-вызовов) — субагент
|
||||
**`kompas-sdk-research`** (Haiku, read-only). Справка живёт **не в репозитории**, а в RAG-базе
|
||||
`kompas-sdk` (MCP-сервер, репозиторий `kompas-sdk-docs`) — грепать по проекту бесполезно, доступ к
|
||||
базе есть только у субагента. Ошибка стоит дёшево: ответ всё равно перепроверяется рефлексией по
|
||||
interop-сборкам.
|
||||
|
||||
## Ориентиры в репозитории
|
||||
|
||||
- Инструменты и их реализация: `src/Kompas.Mcp.Host/Tools/*`, `src/Kompas.Mcp.Core/*`.
|
||||
- Кейсы (полигон подходов): `usecases/` (см. `usecases/README.md`) — в `.gitignore`, в плагин не входит.
|
||||
- Публикуемые навыки плагина: `plugin/skills/` (`kompas-3d`, `kompas-fdm-design`).
|
||||
- Память: `kompas-step-and-assembly-api` (проверенные COM-приёмы импорта/экспорта/обхода сборки).
|
||||
- Инструменты и реализация: `src/Kompas.Mcp.Host/Tools/*`, `src/Kompas.Mcp.Core/*`.
|
||||
- Кейсы (полигон подходов): `usecases/` — локальный, в `.gitignore`, в плагин не входит.
|
||||
- Публикуемые навыки плагина: `plugin/skills/` (`kompas-3d`, `kompas-fdm-design`) — источник истины;
|
||||
`.claude/skills/<навык>` это junction'ы, раскладку делает `tools/sync-agent-assets.ps1`.
|
||||
Перед `git checkout` на ревизию без `plugin/skills` — сначала `-Remove`, иначе checkout ломается
|
||||
на полпути (подробности в `CLAUDE.md`).
|
||||
- Плагин и доставка сервера: `plugin/` (манифест, `server.lock.json`, лаунчер) — версия в
|
||||
`plugin.json` и `server.lock.json` должна совпадать, это проверяет CI.
|
||||
- Память: `kompas-step-and-assembly-api`, `kompas-drawing-api7`, `kompas-hole-api7`,
|
||||
`kompas-formative-features-api5`, `kompas-pattern-mirror-api5`, `kompas-variables-api5`.
|
||||
|
||||
@@ -4,183 +4,470 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project goal
|
||||
|
||||
Build an **MCP (Model Context Protocol) server** that lets an LLM drive the CAD system **КОМПАС-3D** (ASCON). The server exposes КОМПАС operations (create/open documents, build 2D geometry, 3D parts and assemblies, read/write parameters, run macros) as MCP tools.
|
||||
An **MCP (Model Context Protocol) server** that lets an LLM drive the CAD system **КОМПАС-3D** (ASCON).
|
||||
The server is a COM client of an installed КОМПАС: it exposes CAD operations (documents, 2D sketches,
|
||||
3D parts, assemblies, drawings, STEP round-trip, model inspection) as MCP tools over stdio.
|
||||
|
||||
Authoritative references:
|
||||
- **SDK knowledge base in RAG: MCP server `kompas-sdk`** — 2465 articles (one per COM interface/topic) served from the `kompas-sdk-docs` repo, indexed on CT 127 (rag-node). **Not in this repo anymore** — Grep/Read won't find it. For any lookup, dispatch the **`kompas-sdk-research`** subagent (Haiku), which owns the MCP tools; see below.
|
||||
**There is no `docs/` directory** — it was deleted deliberately (commit `b022a91`). This file and
|
||||
[`README.md`](README.md) are the only prose documentation in the repo; verified COM call-chains live
|
||||
in «Key implementation facts» below plus the memory files (`kompas-*-api*.md`). Do not re-create
|
||||
`docs/ARCHITECTURE.md`, `docs/TODO.md`, `docs/OPEN_QUESTIONS.md` — anything worth keeping goes here.
|
||||
|
||||
Authoritative API references:
|
||||
- **SDK knowledge base in RAG: MCP server `kompas-sdk`** — 2465 articles (one per COM interface/topic),
|
||||
served from the separate `kompas-sdk-docs` repo, indexed on CT 127 (rag-node). **Not in this repo** —
|
||||
Grep/Read won't find it. For any lookup dispatch the **`kompas-sdk-research`** subagent (Haiku), which
|
||||
owns the MCP tools.
|
||||
- Online SDK docs: https://help.ascon.ru/KOMPAS_SDK/22/ru-RU/index.html
|
||||
- Local SDK install: `C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK` (headers `Include\ksConstants*.h` are the last word on numeric constant values)
|
||||
- Local SDK install: `C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK` (headers `Include\ksConstants*.h`
|
||||
are the last word on numeric constant values)
|
||||
- `libs/kompas-interop/*.dll` — reflect over these to verify any signature a research answer gives you.
|
||||
|
||||
## Navigating the SDK docs (MCP `kompas-sdk`)
|
||||
### Navigating the SDK base (MCP `kompas-sdk`, via the subagent)
|
||||
|
||||
The base is distilled from the 26 000-page Help&Manual WebHelp export: one article per COM interface/topic under `sdk/interfaces|enums|structures|guides` (+ `resources/` images), each with frontmatter (`title`, `type`, `api`, `domain`, `tags`, `sdk_version`); methods/properties are `## ` headings inside the interface article. Search order:
|
||||
The base is distilled from the 26 000-page Help&Manual WebHelp export: one article per COM
|
||||
interface/topic under `sdk/interfaces|enums|structures|guides` (+ `resources/` images), each with
|
||||
frontmatter (`title`, `type`, `api`, `domain`, `tags`, `sdk_version`); methods/properties are `## `
|
||||
headings inside the interface article. Search order:
|
||||
|
||||
- **`grep_knowledge`** for an exact name — method, interface, enum, constant (`SetSideParam`, `ksHoleTypeEnum`, `o3d_incline`). Method names are `## SetSideParam - …` headings, so the name lands you in the right article. Regex works.
|
||||
- **`search_knowledge`** for task-shaped questions («чем построить массив по сетке»), with filters `type` (`interface`/`enum`/`struct`/`guide`), `tags` (`api7`, `api5`, `3d`, `sketch`, …), `path_prefix` (`sdk/enums/`).
|
||||
- **`get_chunk_context`** to read around a hit; `get_document` only for small articles — `sdk/interfaces/геометрия.md` is 261 KB and will blow the context.
|
||||
- **`grep_knowledge`** for an exact name — method, interface, enum, constant (`SetSideParam`,
|
||||
`ksHoleTypeEnum`, `o3d_incline`). Method names are `## SetSideParam - …` headings. Regex works.
|
||||
- **`search_knowledge`** for task-shaped questions («чем построить массив по сетке»), with filters
|
||||
`type` (`interface`/`enum`/`struct`/`guide`), `tags` (`api7`, `api5`, `3d`, `sketch`, …),
|
||||
`path_prefix` (`sdk/enums/`).
|
||||
- **`get_chunk_context`** to read around a hit; `get_document` only for small articles —
|
||||
`sdk/interfaces/геометрия.md` is 261 KB and will blow the context.
|
||||
- **`knowledge_status`** — indexed commit, document and point counts, when a search looks suspiciously empty.
|
||||
|
||||
It covers the **COM API7/API5** reference this project uses. The cross-platform Qt/C++ **KsAPI** flavour (`ksapi_*`, non-COM binding) is intentionally **excluded** — ignore unless explicitly working with KsAPI.
|
||||
Covers the **COM API7/API5** reference this project uses. The cross-platform Qt/C++ **KsAPI** flavour
|
||||
(`ksapi_*`, non-COM) is intentionally **excluded** — ignore unless explicitly working with KsAPI.
|
||||
|
||||
Updating the base when a new КОМПАС version ships (in `kompas-sdk-docs`, on the machine with КОМПАС installed): `pwsh tools/update_sdk_docs.ps1 -SdkVersion 25` regenerates `sdk/` from `SDK\Help\KOMPAS_SDK_ru-RU.zip`; review the diff (deleted articles = broken parser, not a shrunken API), commit, tag `sdk-vNN`, push — Gitea Actions on `rag-node` revalidates and swaps the `kompas_sdk` alias. Indexing is incremental by checksum.
|
||||
Updating the base when a new КОМПАС ships (in `kompas-sdk-docs`, on the machine with КОМПАС installed):
|
||||
`pwsh tools/update_sdk_docs.ps1 -SdkVersion 25` regenerates `sdk/` from `SDK\Help\KOMPAS_SDK_ru-RU.zip`;
|
||||
review the diff (deleted articles = broken parser, not a shrunken API), commit, tag `sdk-vNN`, push —
|
||||
Gitea Actions on `rag-node` revalidates and swaps the `kompas_sdk` alias. Indexing is incremental by checksum.
|
||||
|
||||
## Current state
|
||||
|
||||
**Status:** v1 + v2 + STEP/assembly + direct B-rep edit + structural inspection + 2D drawings + Claude Code plugin packaging — implemented and working (full sketch→feature→inspect→STEP round-trip + `move_face` + `describe_model` validated end-to-end; server also ships as the `kompas` plugin, see below).
|
||||
Stack: **.NET 8 (`net8.0-windows`, x64), C#**, MCP via the official `ModelContextProtocol` SDK over **stdio**. **84 MCP tools, 381 .NET tests green (250 unit + 131 integration) + 41 Pester tests** for the plugin's PowerShell scripts (not counted in the .NET total).
|
||||
**Status:** working end-to-end. Parts (sketch → feature → inspect), forming ops, patterns/mirror, holes,
|
||||
direct B-rep edit, STEP import/export, assemblies, 2D drawings, structural inspection — implemented and
|
||||
validated live. Also packaged as the Claude Code plugin `kompas` (no release published yet).
|
||||
|
||||
Where to look (single source of truth — do **not** duplicate these lists here):
|
||||
- **Full tool catalog** (by group, all 84) → [`README.md`](README.md) §«Инструменты».
|
||||
- **Roadmap / what's already done** → [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) §10.
|
||||
- **Backlog / what's left** → [`docs/TODO.md`](docs/TODO.md) (canonical; top of «2D-ЧЕРТЁЖ» = next priority. Associative diametral/radial→circle binding (`associate` flag), sheet format (`drawing_set_sheet_format`), leaders (`drawing_add_leader`) done — next: bases, tolerances, arc/angular/rough binding; plus extra mate types, package E continuation).
|
||||
- **Design decisions / caveats** → [`docs/OPEN_QUESTIONS.md`](docs/OPEN_QUESTIONS.md). Known caveat («Ревью v2»): boss/cut direction on a selected face depends on face-normal orientation — `forward` may need flipping; the agent picks via snapshot or `describe_face` normal.
|
||||
- **Verified COM call-chains & gotchas** → «Key implementation facts» below + memory files (`kompas-*-api*.md`).
|
||||
Stack: **.NET 8 (`net8.0-windows`, x64), C#**, MCP via the official `ModelContextProtocol` SDK over **stdio**.
|
||||
**84 MCP tools; 381 .NET tests green (250 unit + 131 integration) + 41 Pester tests** for the plugin's
|
||||
PowerShell (not counted in the .NET total).
|
||||
|
||||
Principle: **MCP = translating SDK capabilities into general tools** (not task-specific). On top of MCP — skill **`.claude/skills/kompas-3d/`** with a methodology (playbooks, heuristics, validated in `usecases/`). Layout: `usecases/` (in .gitignore) is the proving ground; proven techniques are promoted to the skill. A separate skill **`.claude/skills/kompas-fdm-design/`** is a DFM methodology layer for FDM/FFF 3D-printing (overhang rules, wall thickness, teardrop holes, fits/clearances, orientation for strength, elephant foot, bosses/inserts/snap-fits) with a lightweight geometry audit using existing inspection tools; it works on top of `kompas-3d` (the build layer) and exports via `export_step` — no slicer integration.
|
||||
The full tool catalog (by group, all 84) lives in [`README.md`](README.md) §«Инструменты» — the single
|
||||
source of truth; do not duplicate it here.
|
||||
|
||||
**Skill layout / sync** (`tools/sync-agent-assets.ps1`, module `tools/AgentAssets.psm1`): source of truth is `plugin/skills/{kompas-3d,kompas-fdm-design}`; `.claude/skills/<name>` and `.agents/skills/<name>` are **junctions** onto it created by the script (their paths are pinpointed in `.gitignore` — git sees a junction as a plain directory). Modes are mutually exclusive parameter sets (`-Remove -Check` together is a parameter-binding error):
|
||||
```
|
||||
src/Kompas.Mcp.Core/ COM layer: STA dispatcher, session, documents, modeling, drawings, conversion, inspection
|
||||
src/Kompas.Mcp.Host/ MCP stdio server; Tools/*.cs = one file per tool group
|
||||
tests/Kompas.Mcp.Tests/ unit (root) + Integration/ (needs a running КОМПАС)
|
||||
libs/kompas-interop/ ASCON interop DLLs — compile-time only, see «Interop assemblies are not shipped»
|
||||
plugin/ Claude Code plugin `kompas`: manifest, launcher, server lock, skills (source of truth)
|
||||
tools/ skill-junction script + Pester tests
|
||||
.claude/agents/ kompas-sdk-research (Haiku) — the only subagent left in this repo
|
||||
.claude/skills/ junctions onto plugin/skills + kompas-mcp-dev (internal, non-junctioned)
|
||||
```
|
||||
|
||||
```powershell
|
||||
dotnet build -c Release # build
|
||||
dotnet test --filter "Category=Unit" # 250 unit tests, no COM
|
||||
dotnet test --filter "Category=Integration" # 131 integration tests, needs running КОМПАС
|
||||
dotnet run --project src/Kompas.Mcp.Host # start the MCP server (stdio)
|
||||
pwsh -NoProfile -File tools/tests/run-ps-tests.ps1 # 41 Pester tests (plugin PowerShell)
|
||||
```
|
||||
|
||||
Principle: **MCP = translating SDK capabilities into general tools**, never task-specific ones. If a
|
||||
technique is missing, don't hardcode a workaround for the case at hand — prove the mechanism, then add
|
||||
a *general* tool with tests, and describe the method in the `kompas-3d` skill.
|
||||
|
||||
### Backlog (canonical since `docs/TODO.md` is gone)
|
||||
|
||||
- **2D drawing** (top priority): bases (`drawing_add_base`), tolerances, associative binding for
|
||||
arc/angular/rough dimensions (needs its own spike — `ILineDimension` has no `BaseObject`).
|
||||
- **Assembly**: mate types beyond `coincidence`/`distance` — `parallel`, `perpendicular`, `concentric`,
|
||||
`angle`, `tangency` (enum values exist in `MateType.cs`, not verified live).
|
||||
- **Known caveat:** boss/cut direction on a *selected face* depends on the face-normal orientation —
|
||||
`forward` may need flipping. Pick the direction from `describe_face` normal or a snapshot before building.
|
||||
|
||||
## Skills and their layout
|
||||
|
||||
- **`kompas-3d`** — methodology for *building* things through the MCP tools (playbooks, heuristics).
|
||||
- **`kompas-fdm-design`** — DFM layer for FDM/FFF printing (overhang rules, wall thickness, teardrop
|
||||
holes, fits/clearances, orientation for strength, elephant foot, bosses/inserts/snap-fits) plus a
|
||||
light geometry audit via the inspection tools. Builds through `kompas-3d`, exports via `export_step`,
|
||||
no slicer integration.
|
||||
- **`kompas-mcp-dev`** — internal: developing the server itself (the two layers, the `usecases/` proving
|
||||
ground, the checklist for productizing a technique into a general tool, delegation). Lives at
|
||||
`.claude/skills/kompas-mcp-dev/SKILL.md` as a real directory (not a junction) because the published
|
||||
skills must not reference things a plugin user doesn't have.
|
||||
|
||||
**Sync** (`tools/sync-agent-assets.ps1`, module `tools/AgentAssets.psm1`): the source of truth is
|
||||
`plugin/skills/{kompas-3d,kompas-fdm-design}`; `.claude/skills/<name>` and `.agents/skills/<name>` are
|
||||
**junctions** onto it created by the script (their paths are pinpointed in `.gitignore` — git sees a
|
||||
junction as a plain directory). Modes are mutually exclusive parameter sets (`-Remove -Check` together
|
||||
is a parameter-binding error):
|
||||
- no flags — link (`Created`/`AlreadyLinked`)
|
||||
- `-Check` — verify, prints `БИТО` and exits 1 on drift
|
||||
- `-AllowCopy` — fall back to a real copy (`Copied`) if a junction can't be created
|
||||
- `-Remove` — unlink (`Removed`/`Missing`/`Skipped`)
|
||||
|
||||
**Gotcha:** switching branch/commit to one predating `plugin/skills` leaves the junctions dangling and `git checkout` fails mid-way (`unable to create file .claude/skills/kompas-fdm-design/SKILL.md: No such file or directory`) — part of the worktree is already deleted while HEAD stays on the old branch. Recover with `git ls-files --deleted -z | xargs -0 git restore --`.
|
||||
**Gotcha:** switching to a branch/commit predating `plugin/skills` leaves the junctions dangling and
|
||||
`git checkout` fails mid-way (`unable to create file .claude/skills/kompas-fdm-design/SKILL.md: No such
|
||||
file or directory`) — part of the worktree is already deleted while HEAD stays on the old branch.
|
||||
Recover with `git ls-files --deleted -z | xargs -0 git restore --`. Correct order:
|
||||
|
||||
Correct order:
|
||||
1. `pwsh -NoProfile -File tools/sync-agent-assets.ps1 -Remove`
|
||||
2. `git checkout <ref>`
|
||||
3. `pwsh -NoProfile -File tools/sync-agent-assets.ps1` (re-link, if `plugin/skills` exists on the new ref)
|
||||
3. `pwsh -NoProfile -File tools/sync-agent-assets.ps1` (re-link, if `plugin/skills` exists on that ref)
|
||||
|
||||
`-Remove` is safe because it deletes the reparse point itself via `[System.IO.Directory]::Delete` (no `-Recurse`) — never walks into the target, so `plugin/skills` content is untouched, and a real (non-junction) directory is left alone (`Skipped`); it detects a junction by the `ReparsePoint` attribute, not by resolving the target, which is exactly why it still works once the target is gone. No `post-checkout` hook on purpose — it fires after checkout already broke, too late to help; step 3 covers the re-link anyway. Tests: `Describe 'Remove-AgentSkillLink'` (5 cases) in `tools/tests/AgentAssets.Tests.ps1`, run via `pwsh -NoProfile -File tools/tests/run-ps-tests.ps1` — part of 41 Pester tests total across the plugin's PowerShell (`AgentAssets.Tests.ps1` 12, `KompasMcpBootstrap.Tests.ps1` 23, `Launcher.Tests.ps1` 6).
|
||||
`-Remove` is safe: it deletes the reparse point itself via `[System.IO.Directory]::Delete` (no `-Recurse`),
|
||||
never walks into the target, and leaves a real (non-junction) directory alone (`Skipped`); it detects a
|
||||
junction by the `ReparsePoint` attribute rather than by resolving the target, which is exactly why it
|
||||
still works once the target is gone. No `post-checkout` hook on purpose — it fires after checkout already
|
||||
broke, too late to help.
|
||||
|
||||
## Delegation subagent
|
||||
|
||||
**`kompas-sdk-research`** (`.claude/agents/`, model **Haiku**, read-only — MCP `kompas-sdk` tools only):
|
||||
finds an interface/method/enum/constant signature in the SDK knowledge base and returns a compressed
|
||||
summary. Dispatch it for **any** SDK lookup — it holds the only access to the base, which no longer lives
|
||||
in this repo. Its system prompt is in the agent definition, so pass only the question as the prompt.
|
||||
A wrong answer is cheap: re-verify against `libs/kompas-interop/*.dll` by reflection.
|
||||
|
||||
(The former `docs-maintainer` subagent was removed together with `docs/` — edit `README.md`/`CLAUDE.md`
|
||||
directly.)
|
||||
|
||||
## Claude Code plugin (`plugin/`)
|
||||
|
||||
The server and both skills ship as the Claude Code plugin **`kompas`** — `plugin/` is a self-contained tree, distributed independently of the rest of the repo (see «Distribution channel» below).
|
||||
The server and both published skills ship as the plugin **`kompas`** — `plugin/` is a self-contained tree,
|
||||
distributed independently of the rest of the repo.
|
||||
|
||||
- `plugin/.claude-plugin/plugin.json` — manifest: `name: "kompas"`, `version` (currently `0.0.0`). Version is Claude Code's cache key — `/plugin update`/auto-update skip a plugin whose version matches what's already installed, so every release must bump it.
|
||||
- `plugin/server.lock.json` — pins exactly one server version: `{version, sourceSha, url, sha256}`. `0.0.0` with empty fields = no release published yet.
|
||||
- `plugin/skills/{kompas-3d,kompas-fdm-design}/` — **the** source of truth for both skills (see skill-layout section above); `.claude/skills/kompas-mcp-dev/SKILL.md` is a real (non-junctioned) internal skill covering server *development* itself (usecases proving ground, productizing playbooks, subagents) — split out because the published skill edition must not reference things a plugin user doesn't have; marked as a draft pending a repo workflow pass.
|
||||
- `plugin/commands/doctor.md` — `/kompas:doctor`: compares expected version (from the lock) against `KOMPAS_MCP_EXE` override / `install.json` marker / `kompas-mcp.exe --version`, checks whether КОМПАС is installed/running, then `kompas_connect` → `kompas_status`.
|
||||
- `plugin/.mcp.json` — declares the `kompas` MCP server as `powershell -File ${CLAUDE_PLUGIN_ROOT}/scripts/launch-kompas-mcp.ps1`.
|
||||
- `plugin/scripts/KompasMcpBootstrap.psm1` — delivery: `Read-KompasMcpLock`/`Test-KompasMcpInstall`/`Install-KompasMcpServer`/`Resolve-KompasMcpExecutable`. Downloads the release asset, verifies SHA256, unpacks under `%LOCALAPPDATA%\kompas-mcp\.tmp` and atomically moves it to `%LOCALAPPDATA%\kompas-mcp\<version>`, writes an `install.json` marker. An install is only trusted when the marker's `version`/`sha256` match the lock.
|
||||
- `plugin/scripts/launch-kompas-mcp.ps1` — entry point: `KOMPAS_MCP_EXE` env var wins (dev mode), else deliver per the lock; runs the server and forwards its exit code. Invariant: **not a single byte reaches stdout before the server starts** (stdout is the JSON-RPC channel) — all diagnostics go to stderr prefixed `kompas-mcp launcher:`.
|
||||
- `plugin/README.md` — prerequisites and install; `plugin/adapters/{codex,opencode}/` — snippets (not separate distributions) wiring the same launcher into Codex and opencode.
|
||||
- CLI flag `--version` (`Kompas.Mcp.Core/Startup/CliArguments.IsVersionRequest`, early-exit in `Program.cs` before the host/КОМПАС start) — prints the 3-part assembly version and exits; `/kompas:doctor` uses it to cross-check the installed binary against the lock.
|
||||
- `plugin/.claude-plugin/plugin.json` — manifest: `name: "kompas"`, `version` (currently `0.0.0`).
|
||||
Version is Claude Code's cache key — `/plugin update` and auto-update skip a plugin whose version
|
||||
matches what's installed, so **every release must bump it**.
|
||||
- `plugin/server.lock.json` — pins exactly one server version: `{version, sourceSha, url, sha256}`.
|
||||
`0.0.0` with empty fields = no release published yet.
|
||||
- `plugin/skills/{kompas-3d,kompas-fdm-design}/` — the source of truth for both published skills.
|
||||
- `plugin/commands/doctor.md` — `/kompas:doctor`: compares the expected version (from the lock) against
|
||||
the `KOMPAS_MCP_EXE` override / `install.json` marker / `kompas-mcp.exe --version`, checks whether
|
||||
КОМПАС is installed and running, then `kompas_connect` → `kompas_status`.
|
||||
- `plugin/.mcp.json` — declares the `kompas` MCP server as
|
||||
`powershell -File ${CLAUDE_PLUGIN_ROOT}/scripts/launch-kompas-mcp.ps1`.
|
||||
- `plugin/scripts/KompasMcpBootstrap.psm1` — delivery: `Read-KompasMcpLock` / `Test-KompasMcpInstall` /
|
||||
`Install-KompasMcpServer` / `Resolve-KompasMcpExecutable`. Downloads the release asset, verifies SHA256,
|
||||
unpacks under `%LOCALAPPDATA%\kompas-mcp\.tmp` and atomically moves it to
|
||||
`%LOCALAPPDATA%\kompas-mcp\<version>`, writes an `install.json` marker. An install is trusted only when
|
||||
the marker's `version`/`sha256` match the lock.
|
||||
- `plugin/scripts/launch-kompas-mcp.ps1` — entry point: the `KOMPAS_MCP_EXE` env var wins (dev mode),
|
||||
otherwise deliver per the lock; runs the server and forwards its exit code. Invariant: **not a single
|
||||
byte reaches stdout before the server starts** (stdout is the JSON-RPC channel) — all diagnostics go to
|
||||
stderr prefixed `kompas-mcp launcher:`.
|
||||
- `plugin/README.md` — prerequisites and install; `plugin/adapters/{codex,opencode}/` — config snippets
|
||||
(not separate distributions) wiring the same launcher into Codex and opencode.
|
||||
- CLI flag `--version` (`Core/Startup/CliArguments.IsVersionRequest`, early-exit in `Program.cs` before
|
||||
the host and any КОМПАС contact) — prints the 3-part assembly version; `/kompas:doctor` uses it to
|
||||
cross-check the installed binary against the lock.
|
||||
|
||||
**Gotchas:**
|
||||
- **Plugin PowerShell files must be UTF-8 **with BOM**.** The launcher runs under `powershell` (Windows PowerShell 5.1), which reads BOM-less files in the system ANSI codepage — Cyrillic string literals corrupt parsing of the whole script. `pwsh` 7 doesn't reproduce this (BOM auto-detected either way).
|
||||
- Switching branch/commit across a `plugin/skills` boundary can dangle the skill junctions mid-checkout — same failure mode and fix as described in the skill-layout section above.
|
||||
- **Plugin PowerShell files must be UTF-8 *with BOM*.** The launcher runs under `powershell` (Windows
|
||||
PowerShell 5.1), which reads BOM-less files in the system ANSI codepage — Cyrillic string literals then
|
||||
corrupt parsing of the whole script. `pwsh` 7 doesn't reproduce this (BOM auto-detected either way).
|
||||
- Switching branch/commit across a `plugin/skills` boundary can dangle the skill junctions mid-checkout —
|
||||
same failure mode and fix as in the skill-layout section above.
|
||||
|
||||
### Distribution channel — the `dist` branch, not `main`
|
||||
|
||||
Releases (`.gitea/workflows/release.yml`, tag `vX.Y.Z`) `dotnet publish -r win-x64 --self-contained` → zip → idempotent upsert of a Gitea release + asset via the Gitea API → re-verify the downloaded asset's SHA256 → commit updated `server.lock.json`/`plugin.json` to branch **`dist`**, on top of the tagged commit. A private plugin marketplace (`home-repo-cc`) will reference the plugin via a `git-subdir` source pinned to `ref: dist` — **not yet added**, and there is **no release yet**. Rationale: a release is built from the tagged commit; if the marketplace pointed at `main`, a user could get a newer `plugin/` tree under a version whose binary was built from a different (older) commit. `.gitea/workflows/ci.yml` runs on push/PR to `main`: build + unit tests (`Category=Unit&Requires!=Windows`, explicit project paths — see gotcha below) + consistency check between `plugin.json` and `server.lock.json`. Runner: `gitea-runner` (docker executor, label `ubuntu-latest`).
|
||||
Releases (`.gitea/workflows/release.yml`, tag `vX.Y.Z`): `dotnet publish -r win-x64 --self-contained` →
|
||||
zip → idempotent upsert of a Gitea release + asset via the Gitea API → re-verify the downloaded asset's
|
||||
SHA256 → commit updated `server.lock.json`/`plugin.json` to branch **`dist`**, on top of the tagged commit.
|
||||
A private plugin marketplace (`home-repo-cc`) will reference the plugin via a `git-subdir` source pinned to
|
||||
`ref: dist` — **not added yet**, and there is **no release yet**. Rationale: a release is built from the
|
||||
tagged commit; if the marketplace pointed at `main`, a user could get a newer `plugin/` tree under a
|
||||
version whose binary was built from an older commit.
|
||||
|
||||
Extra CI gotchas:
|
||||
- **SDK 8 doesn't understand the `.slnx` solution format** (`MSBUILD : error MSB1003`) — CI builds/tests explicit project paths (`dotnet build src/Kompas.Mcp.Host/Kompas.Mcp.Host.csproj`, `dotnet test tests/Kompas.Mcp.Tests/Kompas.Mcp.Tests.csproj`) instead of the solution. Not reproducible locally with SDK 9+.
|
||||
- **`net8.0-windows` builds and runs unit tests fine on the Linux runner** with `-p:EnableWindowsTargeting=true` — confirmed by a green CI run (`Passed! Failed: 0, Passed: 221`; 245 after the interop-resolver tests). ASCON interop DLLs link as ordinary references; `ole32`/`oleaut32` are only needed at COM runtime, not build time. 5 `DispatcherTests` are tagged `[Trait("Requires", "Windows")]` and excluded — `Thread.SetApartmentState(STA)` throws `PlatformNotSupportedException` on Linux.
|
||||
- **`.gitignore`'s `.mcp.json` rule is anchored to the repo root** (`/.mcp.json`) — without the leading slash the glob also matched the tracked, portable `plugin/.mcp.json`.
|
||||
- Integration tests never run in CI (require a running КОМПАС with GUI and a license) — this is a hard boundary, not a future task.
|
||||
`.gitea/workflows/ci.yml` runs on push/PR to `main`: `plugin.json`↔`server.lock.json` consistency check
|
||||
(jq), build, then unit tests filtered `Category=Unit&Requires!=Windows` (245 of the 250). Runner:
|
||||
`gitea-runner` (docker executor, label `ubuntu-latest`).
|
||||
|
||||
**Delegation subagents** (`.claude/agents/`, dispatched via the `Agent` tool to keep heavy work off Opus's context — system prompt lives in the agent definition, so pass only the variable part as the prompt):
|
||||
- **`kompas-sdk-research`** (model: **Haiku**, read-only — MCP `kompas-sdk` tools only): finds an interface/method/enum/constant signature in the SDK knowledge base and returns a compressed summary. Dispatch it for **any** SDK lookup — it holds the only access to the base, which no longer lives in this repo; a wrong answer is cheap — Opus re-verifies against `libs/kompas-interop/*.dll` by reflection.
|
||||
- **`docs-maintainer`** (model: **Sonnet**, `Read`/`Edit`/`Write`/`Glob`/`Grep`): syncs `README.md`/`CLAUDE.md`/`docs/ARCHITECTURE.md`/`docs/OPEN_QUESTIONS.md`/`docs/presentation.html` with code changes from a change summary you provide. Dispatch it for doc updates instead of editing docs by hand. (Both were skills before — converted to native subagents; the model split reflects task type: bounded retrieval → Haiku, judgement-heavy editing → Sonnet.)
|
||||
|
||||
Layout: `src/Kompas.Mcp.Core` (COM layer), `src/Kompas.Mcp.Host` (MCP stdio server + tools),
|
||||
`tests/Kompas.Mcp.Tests` (unit + integration), `libs/kompas-interop/*.dll` (КОМПАС interop
|
||||
assemblies from SDK `Samples/Common`, referenced via `Directory.Build.props` → `KompasInteropDir`
|
||||
— **compile-time only**, see «Interop assemblies are not shipped» below).
|
||||
|
||||
```powershell
|
||||
dotnet build -c Release # build
|
||||
dotnet test --filter "Category=Unit" # unit tests (no COM)
|
||||
dotnet test --filter "Category=Integration" # integration (requires running КОМПАС)
|
||||
dotnet run --project src/Kompas.Mcp.Host # start the MCP server (stdio)
|
||||
```
|
||||
CI gotchas:
|
||||
- **SDK 8 doesn't understand the `.slnx` solution format** (`MSBUILD : error MSB1003`) — CI builds and
|
||||
tests explicit project paths (`src/Kompas.Mcp.Host/Kompas.Mcp.Host.csproj`,
|
||||
`tests/Kompas.Mcp.Tests/Kompas.Mcp.Tests.csproj`) instead of the solution. Not reproducible locally with SDK 9+.
|
||||
- **`net8.0-windows` builds and runs unit tests fine on the Linux runner** with
|
||||
`-p:EnableWindowsTargeting=true`. ASCON interop DLLs link as ordinary references; `ole32`/`oleaut32` are
|
||||
only needed at COM runtime, not build time. The 5 `DispatcherTests` are tagged
|
||||
`[Trait("Requires", "Windows")]` and excluded — `Thread.SetApartmentState(STA)` throws
|
||||
`PlatformNotSupportedException` on Linux.
|
||||
- **`.gitignore`'s `.mcp.json` rule is anchored to the repo root** (`/.mcp.json`) — without the leading
|
||||
slash the glob also matched the tracked, portable `plugin/.mcp.json`.
|
||||
- Integration tests never run in CI (they need a running КОМПАС with GUI and a license) — a hard boundary,
|
||||
not a future task.
|
||||
|
||||
## Key implementation facts (don't relearn)
|
||||
|
||||
- **Interop assemblies are not shipped** (`KompasAPI7`, `Kompas6API5`, `Kompas6Constants`, `Kompas6Constants3D` belong to ASCON). `libs/kompas-interop` is referenced with `<Private>false</Private>` — compile-time only, absent from `bin/` **and** from the published archive/`deps.json`. At runtime `KompasInteropLoader.Install()` (first line of `Program.Main`) hooks `AssemblyLoadContext.Default.Resolving`; lookup order: `KOMPAS_INTEROP_DIR` → next to `kompas-mcp.exe` → cache → the installed КОМПАС. **The install has no loose interop DLLs** — they live only inside `<install>\SDK\Samples\CSharp.zip` under `Common/`, so the provider unzips them into `%LOCALAPPDATA%\kompas-mcp\interop\<key>` (key = hash of the zip's path+size+mtime, so a КОМПАС upgrade re-extracts; staging dir + `Directory.Move` for atomicity). Install dir comes from `HKCR\<ProgID>\CLSID` → `HKCR\CLSID\{…}\LocalServer32` (`"…\Bin\kHome.Exe"` on v24 Home) — `HKLM\SOFTWARE\ASCON` holds settings, not the path. All interop identities are `Version=1.0.0.0` with **no strong name**, so resolving by simple name is version-agnostic across КОМПАС releases. Failure throws `KompasInteropException` with an actionable message; `Program` logs it at startup and `kompas_status` returns it (it checks interop **before** touching `KompasSession` — otherwise the MCP SDK shows only «An error occurred invoking …»). **The test project copies the DLLs itself** (`<None Include="$(KompasInteropDir)\*.dll">`) — the Linux CI runner has no КОМПАС to resolve from.
|
||||
- **All COM calls run on one dedicated STA thread** (`KompasDispatcher.InvokeAsync`); КОМПАС is single-instance STA. Services never touch COM off that thread.
|
||||
- **Connection**: API5 (`KOMPAS.Application.5` → `KompasObject`) then `ksGetApplication7()` → `IApplication`; the API5 root is needed for `ksPart` 3D-building and `ksDocument3D` snapshots. API7 is used for app/documents, holes, assemblies, drawings, direct B-rep edit.
|
||||
- **3D via API5 `ksPart`**: `NewEntity(o3d_*)` + matching definition; always check `entity.Create()` return. Entity codes: `o3d_sketch`/`o3d_bossExtrusion`/`o3d_cutExtrusion`/`o3d_bossRotated`/`o3d_fillet`/`o3d_chamfer`, `o3d_shellOperation=43`, `o3d_ribOperation=44`, `o3d_baseEvolution=45` (sweep), `o3d_baseLoft=30`, `o3d_planeOffset=14`, `o3d_meshCopy=35` (linear pattern), `o3d_circularCopy=36`, `o3d_mirrorOperation=48`, `o3d_mirrorAllOperation=49`, `o3d_incline=42` (draft), axes `o3d_axisOX/OY/OZ=71/72/73`. Definitions: `ksBossExtrusionDefinition`/`ksCutExtrusionDefinition`/`ksBossRotatedDefinition`/`ksFilletDefinition`/`ksChamferDefinition`/`ksShellDefinition`/`ksRibDefinition`/`ksBaseEvolutionDefinition`/`ksBaseLoftDefinition`/`ksPlaneOffsetDefinition`/`ksMeshCopyDefinition`/`ksCircularCopyDefinition`/`ksMirrorCopyDefinition`/`ksMirrorCopyAllDefinition`/`ksInclineDefinition`. Cut-through holes use `dtBoth` + `etThroughAll`.
|
||||
- **Face/edge selection**: `ksPart.EntityCollection(o3d_face|o3d_edge)` → `SelectByPoint(x,y,z)` (world mm) → `GetByIndex(0)`; or by stable index from `list_faces`/`list_edges` (helpers `SelectFaceByIndex`/`SelectEdgeByIndex`, range-checked before mutation; by-index more reliable than by-point). Classify: `ksFaceDefinition.IsPlanar/IsCylinder/…` + `GetArea(ST_MIX_MM)`; `ksEdgeDefinition.IsLineSeg/IsCircle/IsArc/…` + `GetLength()`. Sketch axis = line with system style **3** (осевая).
|
||||
- **МЦХ / mass props**: use **API5 `ksPart.CalcMassInertiaProperties(ST_MIX_MM|ST_MIX_KG)`** → `ksMassInertiaParam` (`v`/`m`/`F`/`xc`/`yc`/`zc`), computed on demand. **Do NOT** use API7 `IMassInertiaParam7.Calculate()` — its `Actual` flag sticks after the first calc and won't refresh on API5 geometry changes (stale volume). Bounding box: `ksPart.GetGabarit(full:false, …)`.
|
||||
- **Snapshot**: `ksDocument3D.SaveAsToRasterFormat(file, ksRasterFormatParam)` renders to a temp file (the in-memory `resultArrayBytes` stays empty when a filename is given); read bytes back. Return to MCP via `ImageContentBlock.FromBytes(bytes, mime)` — **not** `Data = bytes` (Data holds base64 bytes). Use `model_snapshot` only for visually-spatial questions; **prefer `describe_model` first** (structural «passport» — bbox + МЦХ + bodies + topology + feature tree + variables, no image token cost).
|
||||
- Logs go to **stderr** (stdout is the MCP channel). Integration tests reuse one КОМПАС via `KompasFixture`; artifacts land in gitignored `.scratch/`. All integration test classes inherit `IntegrationTestBase` (`IAsyncLifetime`) → `DocumentService.CloseAllAsync` after each test (prevents tab accumulation).
|
||||
- **Feature tree API**: read via API5 `ksPart.GetFeature()` → `(ksFeature).SubFeatureCollection(true,false)` → `ksFeatureCollection`. Cast `(ksFeature)part` does NOT work (RCW QI returns null) — must use `GetFeature()`. `ksFeature.type` returns only coarse `o3d_entity` and does NOT distinguish operations; precise type and params come from `ksFeature.GetObject()` → `ksEntity.GetDefinition()` + the matching definition (`ksBossExtrusionDefinition.GetSideParam`, `ksFilletDefinition.radius`, `ksChamferDefinition.GetChamferParam`, …). Node names ("Элемент выдавливания:1") are localized by КОМПАС. Features registered by id in `_features` support patterns/mirror; helpers `RequireSketch`/`RequireFeature`.
|
||||
- **Variables (API5)**: create/read via `ksPart.GetFeature().VariableCollection` (property on root ksFeature) — **NOT** `ksPart.VariableCollection()` (returns external-only). Expression is the leading field; value is derived. Apply via `ksPart.RebuildModel()` (check return); after rebuild the RCW variable is stale → re-read via a fresh collection (`ReadValueFresh`). Dependents recalculate automatically; deletion fails if dependents exist (`RemoveVariable` returns FALSE). **Geometry limitation:** variables drive geometry only in a parametric model (sketch dims linked to variable names); our sketches use literal coords → `set_variable` stores/computes the value but does NOT move geometry. Parametric sketches **unavailable via COM** (`ksCDimWithVariable` not constructible from external automation — see `docs/superpowers/specs/2026-05-27-parametric-sketch-findings.md`; do not retry without new info).
|
||||
- **Topology / measure API**: `ksFaceDefinition.EdgeCollection` / `GetCylinderParam` / `GetSurface().GetNormal` + `normalOrientation`; `ksEdgeDefinition.GetAdjacentFace(bool)` / `GetVertex(bool)` → `ksVertexDefinition.GetPoint`; bodies via `ksPart.BodyCollection()` → `ksBody` (`IsSolid` / `FaceCollection`); measurements via `ksPart.GetMeasurer()` → `ksMeasurer` (`SetObject1/2`, `unit=ST_MIX_MM`, `Calc`, `distance`/`MinDistance`/`angle` in degrees / `IsAngleValid`).
|
||||
- **"STEP import without history"** detected structurally: `bodyCount > 0 && no formative features && features.Count <= bodyCount+1` (only origin + body). STEP with edits ("Смещённая плоскость" / "Разрезать" / "Переместить грани" / "Булева операция") already has history.
|
||||
- **STEP import/export COM pattern** (verified): `IApplication.get_Converter((object)(int)ksConverterFromSTEP=-3)` (pass format code, not DLL path) → `IConverter.ConverterParameters(cmd)` → `IAdditionConvertParameters.Format=ksConverterFromSTEP` → `IKompasDocument3D1.ConvertFromAdditionFormat(path, prm)`. Export: `ConvertToAdditionFormat` (format codes AP203/AP214/AP242). Param co-classes (AdditionConvertParameters etc.) are NOT CoCreatable — only via converter factories. Assembly traversal (`list_components`): `IKompasDocument3D.TopPart → IPart7.Parts (IParts7)`. Extract component: `prm.NeedCreateComponentsFiles=true` (writes .m3d files next to STEP) + `IPart7.OpenSourceDocument`.
|
||||
- **Direct B-rep face editing (`move_face`)** (verified): API7 face via `IPart7.FindObjectsByPoint(x,y,z,true)` → cast to `KompasAPI7.IFace`; containers via runtime COM-QI `(ISurfaceContainer)part`, `(IModelContainer)part`; `FaceMover` (SetFaces + Offset + Direction + Update) moves the face. distance>0 = outward (add material), <0 = inward. Works on parametric and imported B-rep. Full split-reposition workflow (SplitSolids/BodyRepositions) is driveable; only partly exposed as tools (`split_solid_by_plane`, `move_body`, `boolean_union`).
|
||||
- **Interop assemblies are not shipped** (`KompasAPI7`, `Kompas6API5`, `Kompas6Constants`,
|
||||
`Kompas6Constants3D` belong to ASCON). `libs/kompas-interop` is referenced with `<Private>false</Private>`
|
||||
— compile-time only, absent from `bin/` **and** from the published archive/`deps.json`. At runtime
|
||||
`KompasInteropLoader.Install()` (first line of `Program.cs`) hooks `AssemblyLoadContext.Default.Resolving`;
|
||||
lookup order: `KOMPAS_INTEROP_DIR` → next to `kompas-mcp.exe` → cache → the installed КОМПАС.
|
||||
**The install has no loose interop DLLs** — they live only inside `<install>\SDK\Samples\CSharp.zip`
|
||||
under `Common/`, so the provider unzips them into `%LOCALAPPDATA%\kompas-mcp\interop\<key>` (key = hash
|
||||
of the zip's path+size+mtime, so a КОМПАС upgrade re-extracts; staging dir + `Directory.Move` for
|
||||
atomicity). The install dir comes from `HKCR\<ProgID>\CLSID` → `HKCR\CLSID\{…}\LocalServer32`
|
||||
(`"…\Bin\kHome.Exe"` on v24 Home) — `HKLM\SOFTWARE\ASCON` holds settings, not the path. All interop
|
||||
identities are `Version=1.0.0.0` with **no strong name**, so resolving by simple name is version-agnostic
|
||||
across КОМПАС releases. Failure throws `KompasInteropException` with an actionable message; `Program`
|
||||
logs it at startup (non-fatal — `--version` must work without КОМПАС) and `kompas_status` returns it
|
||||
(it checks interop **before** touching `KompasSession`, otherwise the MCP SDK shows only «An error
|
||||
occurred invoking …»). **The test project copies the DLLs itself**
|
||||
(`<None Include="$(KompasInteropDir)\*.dll">`) — the Linux CI runner has no КОМПАС to resolve from.
|
||||
- **All COM calls run on one dedicated STA thread** (`KompasDispatcher.InvokeAsync`); КОМПАС is
|
||||
single-instance STA. Services never touch COM off that thread.
|
||||
- **Connection**: API5 (`KOMPAS.Application.5` → `KompasObject`) then `ksGetApplication7()` → `IApplication`;
|
||||
the API5 root is needed for `ksPart` 3D building and `ksDocument3D` snapshots. API7 drives app/documents,
|
||||
holes, assemblies, drawings and direct B-rep edit.
|
||||
- **3D via API5 `ksPart`**: `NewEntity(o3d_*)` + matching definition; always check `entity.Create()` return.
|
||||
Entity codes: `o3d_sketch`/`o3d_bossExtrusion`/`o3d_cutExtrusion`/`o3d_bossRotated`/`o3d_fillet`/`o3d_chamfer`,
|
||||
`o3d_shellOperation=43`, `o3d_ribOperation=44`, `o3d_baseEvolution=45` (sweep), `o3d_baseLoft=30`,
|
||||
`o3d_planeOffset=14`, `o3d_meshCopy=35` (linear pattern), `o3d_circularCopy=36`, `o3d_mirrorOperation=48`,
|
||||
`o3d_mirrorAllOperation=49`, `o3d_incline=42` (draft), axes `o3d_axisOX/OY/OZ=71/72/73`. Definitions:
|
||||
`ksBossExtrusionDefinition`/`ksCutExtrusionDefinition`/`ksBossRotatedDefinition`/`ksFilletDefinition`/
|
||||
`ksChamferDefinition`/`ksShellDefinition`/`ksRibDefinition`/`ksBaseEvolutionDefinition`/
|
||||
`ksBaseLoftDefinition`/`ksPlaneOffsetDefinition`/`ksMeshCopyDefinition`/`ksCircularCopyDefinition`/
|
||||
`ksMirrorCopyDefinition`/`ksMirrorCopyAllDefinition`/`ksInclineDefinition`. Cut-through holes use
|
||||
`dtBoth` + `etThroughAll`.
|
||||
- **Face/edge selection**: `ksPart.EntityCollection(o3d_face|o3d_edge)` → `SelectByPoint(x,y,z)` (world mm)
|
||||
→ `GetByIndex(0)`; or by stable index from `list_faces`/`list_edges` (helpers `SelectFaceByIndex`/
|
||||
`SelectEdgeByIndex`, range-checked before mutation; by-index is more reliable than by-point). Classify:
|
||||
`ksFaceDefinition.IsPlanar/IsCylinder/…` + `GetArea(ST_MIX_MM)`; `ksEdgeDefinition.IsLineSeg/IsCircle/IsArc/…`
|
||||
+ `GetLength()`. A sketch axis is a line with system style **3** (осевая).
|
||||
- **МЦХ / mass props**: use **API5 `ksPart.CalcMassInertiaProperties(ST_MIX_MM|ST_MIX_KG)`** →
|
||||
`ksMassInertiaParam` (`v`/`m`/`F`/`xc`/`yc`/`zc`), computed on demand. **Do NOT** use API7
|
||||
`IMassInertiaParam7.Calculate()` — its `Actual` flag sticks after the first calc and won't refresh on
|
||||
API5 geometry changes (stale volume). Bounding box: `ksPart.GetGabarit(full:false, …)`.
|
||||
- **Snapshot**: `ksDocument3D.SaveAsToRasterFormat(file, ksRasterFormatParam)` renders to a temp file (the
|
||||
in-memory `resultArrayBytes` stays empty when a filename is given); read the bytes back. Return to MCP via
|
||||
`ImageContentBlock.FromBytes(bytes, mime)` — **not** `Data = bytes` (Data holds base64). Use
|
||||
`model_snapshot` only for visually-spatial questions; **prefer `describe_model` first** (structural
|
||||
«passport» — bbox + МЦХ + bodies + topology + feature tree + variables, no image token cost).
|
||||
- Logs go to **stderr** (stdout is the MCP channel). Integration tests reuse one КОМПАС via `KompasFixture`;
|
||||
artifacts land in gitignored `.scratch/`. All integration test classes inherit `IntegrationTestBase`
|
||||
(`IAsyncLifetime`) → `DocumentService.CloseAllAsync` after each test (prevents tab accumulation).
|
||||
- **Feature tree API**: read via API5 `ksPart.GetFeature()` → `(ksFeature).SubFeatureCollection(true,false)`
|
||||
→ `ksFeatureCollection`. Casting `(ksFeature)part` does NOT work (RCW QI returns null) — must use
|
||||
`GetFeature()`. `ksFeature.type` returns only a coarse `o3d_entity` and does NOT distinguish operations;
|
||||
the precise type and params come from `ksFeature.GetObject()` → `ksEntity.GetDefinition()` + the matching
|
||||
definition (`ksBossExtrusionDefinition.GetSideParam`, `ksFilletDefinition.radius`,
|
||||
`ksChamferDefinition.GetChamferParam`, …). Node names («Элемент выдавливания:1») are localized by КОМПАС.
|
||||
Features registered by id in `_features` support patterns/mirror; helpers `RequireSketch`/`RequireFeature`.
|
||||
- **Variables (API5)**: create/read via `ksPart.GetFeature().VariableCollection` (property on the root
|
||||
ksFeature) — **NOT** `ksPart.VariableCollection()` (returns external-only). Expression is the leading
|
||||
field; value is derived. Apply via `ksPart.RebuildModel()` (check the return); after a rebuild the RCW
|
||||
variable is stale → re-read via a fresh collection (`ReadValueFresh`). Dependents recalculate
|
||||
automatically; deletion fails while dependents exist (`RemoveVariable` returns FALSE).
|
||||
**Geometry limitation:** variables drive geometry only in a parametric model (sketch dims linked to
|
||||
variable names); our sketches use literal coordinates → `set_variable` stores/computes the value but does
|
||||
NOT move geometry. Parametric sketches are **unavailable via COM** (`ksCDimWithVariable` is not
|
||||
constructible from external automation — see memory `kompas-parametric-sketch-findings`; do not retry
|
||||
without new information).
|
||||
- **Topology / measure API**: `ksFaceDefinition.EdgeCollection` / `GetCylinderParam` /
|
||||
`GetSurface().GetNormal` + `normalOrientation`; `ksEdgeDefinition.GetAdjacentFace(bool)` /
|
||||
`GetVertex(bool)` → `ksVertexDefinition.GetPoint`; bodies via `ksPart.BodyCollection()` → `ksBody`
|
||||
(`IsSolid` / `FaceCollection`); measurements via `ksPart.GetMeasurer()` → `ksMeasurer` (`SetObject1/2`,
|
||||
`unit=ST_MIX_MM`, `Calc`, `distance`/`MinDistance`/`angle` in degrees / `IsAngleValid`).
|
||||
- **«STEP import without history»** is detected structurally: `bodyCount > 0 && no formative features &&
|
||||
features.Count <= bodyCount+1` (only origin + body). A STEP that has been edited («Смещённая плоскость» /
|
||||
«Разрезать» / «Переместить грани» / «Булева операция») already has history.
|
||||
- **STEP import/export COM pattern** (verified): `IApplication.get_Converter((object)(int)ksConverterFromSTEP=-3)`
|
||||
(pass the format code, not a DLL path) → `IConverter.ConverterParameters(cmd)` →
|
||||
`IAdditionConvertParameters.Format=ksConverterFromSTEP` → `IKompasDocument3D1.ConvertFromAdditionFormat(path, prm)`.
|
||||
Export: `ConvertToAdditionFormat` (format codes AP203/AP214/AP242). Param co-classes
|
||||
(`AdditionConvertParameters` etc.) are NOT CoCreatable — only via the converter factories. Assembly
|
||||
traversal (`list_components`): `IKompasDocument3D.TopPart → IPart7.Parts (IParts7)`. Extract a component:
|
||||
`prm.NeedCreateComponentsFiles=true` (writes .m3d files next to the STEP) + `IPart7.OpenSourceDocument`.
|
||||
- **Direct B-rep face editing (`move_face`)** (verified): API7 face via
|
||||
`IPart7.FindObjectsByPoint(x,y,z,true)` → cast to `KompasAPI7.IFace`; containers via runtime COM-QI
|
||||
`(ISurfaceContainer)part`, `(IModelContainer)part`; `FaceMover` (SetFaces + Offset + Direction + Update)
|
||||
moves the face. distance>0 = outward (adds material), <0 = inward. Works on parametric and imported B-rep.
|
||||
The full split-reposition workflow (SplitSolids/BodyRepositions) is driveable; only partly exposed as tools
|
||||
(`split_solid_by_plane`, `move_body`, `boolean_union`).
|
||||
- **Forming ops (API5 definitions)**:
|
||||
- `shell` (`ksShellDefinition`, o3d_shellOperation=43): `thickness` + `thinType` (bool, `= !outward`) + `FaceArray()` (open faces ≥1 by index). Transient RCWs intentionally not released (consistent with Fillet/Chamfer).
|
||||
- `rib` (`ksRibDefinition`, =44): `SetSketch(sketch)` → `index=0`, `angle`, `side` (`left|right|up|down` → 0/1/2/3 via `SketchGeometry.RibSide()`); `SetThinParam(dtBoth, t/2, t/2)` if symmetric, else `(dtNormal, t, 0)`. Empirical: the rib contour must float in the gap (not touch the body) — КОМПАС extends the web automatically.
|
||||
- `sweep` (`ksBaseEvolutionDefinition`, =45): `SetSketch(profile)` → `PathPartArray().Add(path)` → `sketchShiftType=0` → `SetThinParam(false)` → `Create()`. Profile & path on different (usually ⟂) planes; `profileSketchId != pathSketchId`.
|
||||
- `loft` (`ksBaseLoftDefinition`, =30): `Sketchs().Add(each)` → `SetLoftParam(false,false,true)` → `SetThinParam(false)`. ≥2 closed sections on parallel planes — use `sketch_create_on_offset_plane` for non-base sections.
|
||||
- offset plane (`sketch_create_on_offset_plane`, `ksPlaneOffsetDefinition`, o3d_planeOffset=14): `SetPlane(base)` + `offset` mm + `direction` → `Create()` → sketch on it (`OpenSketchOnOffsetPlaneAsync`).
|
||||
- `draft` (`ksInclineDefinition`, o3d_incline=42 — API5 calls the op **Incline**, NOT Draft): `FaceArray()` (by index) + `SetPlane(neutral base plane)` + `angle` (degrees) + `direction`. **Empirical, opposite to SDK docs:** `direction=false`=expanding/outward (adds material), `true`=tapering/inward → code maps `def.direction = !outward`. Validate `0<angle<90`. (`o3d_DraftFromEdges=644` / `IDraftFromEdges` is a different op — not used.)
|
||||
- **Patterns & mirror (API5)**: `linear_pattern` (`ksMeshCopyDefinition`, =35): `SetAxis1(axis)` + `SetCopyParamAlongAxis(true,0,count,step,false)` + `count2=1` (disables 2nd direction → 1D); features via `OperationArray()`. `circular_pattern` (`ksCircularCopyDefinition`, =36): `SetAxis(axis)`; `count1=1` (radial off), `count2=count` (ring), `step2=angle°` between neighbours, `factor2=false`, `inverce=reverse`; `GetOperationArray().Add(feature)`. `mirror_operation` (`ksMirrorCopyDefinition`, =48): `SetPlane(base)` + `GetOperationArray().Add(feature)`. `mirror_body` (`ksMirrorCopyAllDefinition`, =49): `SetPlane(base)`; `ChooseBodies()` casts to null in interop and is NOT needed — mirrors all bodies keeping the original (volume doubles). Empirical: `count` includes the original instance (count=3 → 3 instances). Axes: `CoordinateAxis{X,Y,Z}` → `o3d_axisOX/OY/OZ` (`Core/Modeling/CoordinateAxis.cs`, analogous to `BasePlane.cs`).
|
||||
- **Hole ops (API7 only — `ksHoleDefinition` absent in API5 interop; `HoleService`)**: `HoleCore(ksHoleTypeEnum holeType, Action<IHole3D> configure)` — `configure` sets `HoleParameters` **after** `HoleType` is assigned (the type-specific sub-interface is inaccessible until the type is fixed). Placement shared: `IModelContainer`/`IHole3D`/`IHoleDisposal` + `Points3D`, direction chosen by volume delta, orphan rollback. None registered in `_features` → no id returned. `hole`=`ksHTBase`, configure=null. `hole_counterbore`=`ksHTCounterbore` + `(ISpotfacingHoleParameters)` (`SpotfacingDiameter`/`SpotfacingDepth`). `hole_countersink`=`ksHTCountersinking` + `(ICountersinkHoleParameters)` (`CountersinkType=ksCTDiameterAngle`, `CountersinkDiameter`/`Angle`). `hole_conic`=`ksHTConic` + `(IConicHoleParameters)` (`ConicType=ksCNAngle`, `ConicAngle`).
|
||||
- **Assembly (API7, `AssemblyService`, namespace `Kompas.Mcp.Core.Assemblies`)**: `assembly_add_component` — `IParts7.AddFromFile(path, ExternalFile=true, Redraw=true)` → `IPart7.Placement` (`IPlacement3D`) → `SetOrigin(x,y,z)` (mm, assembly world CS) → `UpdatePlacement(true)` → `top.RebuildModel(true)`. **Empirical:** `UpdatePlacement` returns FALSE for a manually-positioned component (no mates) — NOT an error. Validate `DocumentType==ksDocumentAssembly` before touching `TopPart`; rollback via `IFeature7.Delete` + `RebuildModel`. `assembly_add_mate` — `top.MateConstraints` (`IMateConstraints3D`) → `Add(MateConstraintType)` → `BaseObject1/2` (faces via `top.FindObjectsByPoint(x,y,z,FirstLevel=false)`) → `ParamValue` (distance) → `Update()` → `RebuildModel(true)`; check `mate.Valid` (false → rollback `mate.Owner.Delete`). Types `coincidence`(mc_Coincidence=0) / `distance`(mc_Distance=5, value>0) verified live; `parallel`/`perpendicular`/`concentric`/`angle`/`tangency` — future.
|
||||
- **Drawing (API7, `DrawingService`, namespace `Kompas.Mcp.Core.Drawings`)** — entry via `IKompasDocument2D`; requires an active drawing doc (`DocumentType==ksDocumentDrawing`). All coords are **view-local** (mm) unless noted. Model file must be saved before placing views.
|
||||
- `drawing_create_standard_views`: `doc2d.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди", projTypes, x, y, scale, dx=20, dy=20)` where `projTypes=object[]{1,3,5}` (Front/Up/Left as SAFEARRAY VT_I4). Success: `created==3`; content check: sum of new views' `IView.ObjectCount` > 0 else rollback `IView.Delete`. Returns `ViewNumbers` (the `IView.Number` values — address views by these when placing annotations).
|
||||
- `drawing_fill_title_block`: `doc2d.LayoutSheets.ItemByNumber[1].Stamp` (`IStamp`) → `stamp.Text[columnId].Str = text` (indexed property; `IText.Str` overwrites, no Clear needed) → `stamp.Update()`. Fields→columns (`ksStampEnum`): Name=1, Designation=2, Material=3. Non-transactional (all cells → single `Update`).
|
||||
- **Dimensions go into `(ISymbols2DContainer)view`, NOT the sheet, and are NOT counted in `IView.ObjectCount`** (that counts geometry in `IDrawingContainer`) — verify placement via the container's `*.Count`. Common flow: `Add()` → set coords → `AutoNominalValue=true` → `Update()` → check `Valid` → read `((IDimensionText)dim).NominalValue`; rollback `dim.Delete()` on FALSE/invalid/zero. Leader/angle in **radians** (`DimensionAngles.ToRadians`). Free placement = set coords directly; **associative** (diametral/radial only) = set `BaseObject` to a projected circle → value read from geometry (see below).
|
||||
- linear (`ILineDimension`): `X1,Y1,X2,Y2` (extension pts) + `X3,Y3` (dim line) + `Orientation` (`DimensionOrientation{Horizontal,Vertical,Parallel}` → `ksLinDParallel=0`/`Horizontal=1`/`Vertical=2`; parallel needs no explicit Angle).
|
||||
- `shell` (`ksShellDefinition`, o3d_shellOperation=43): `thickness` + `thinType` (bool, `= !outward`) +
|
||||
`FaceArray()` (≥1 open face by index). Transient RCWs intentionally not released (consistent with
|
||||
Fillet/Chamfer).
|
||||
- `rib` (`ksRibDefinition`, =44): `SetSketch(sketch)` → `index=0`, `angle`, `side`
|
||||
(`left|right|up|down` → 0/1/2/3 via `SketchGeometry.RibSide()`); `SetThinParam(dtBoth, t/2, t/2)` if
|
||||
symmetric, else `(dtNormal, t, 0)`. Empirical: the rib contour must float in the gap (not touch the body)
|
||||
— КОМПАС extends the web automatically.
|
||||
- `sweep` (`ksBaseEvolutionDefinition`, =45): `SetSketch(profile)` → `PathPartArray().Add(path)` →
|
||||
`sketchShiftType=0` → `SetThinParam(false)` → `Create()`. Profile and path on different (usually ⟂)
|
||||
planes; `profileSketchId != pathSketchId`.
|
||||
- `loft` (`ksBaseLoftDefinition`, =30): `Sketchs().Add(each)` → `SetLoftParam(false,false,true)` →
|
||||
`SetThinParam(false)`. ≥2 closed sections on parallel planes — use `sketch_create_on_offset_plane` for
|
||||
non-base sections.
|
||||
- offset plane (`sketch_create_on_offset_plane`, `ksPlaneOffsetDefinition`, o3d_planeOffset=14):
|
||||
`SetPlane(base)` + `offset` mm + `direction` → `Create()` → sketch on it (`OpenSketchOnOffsetPlaneAsync`).
|
||||
- `draft` (`ksInclineDefinition`, o3d_incline=42 — API5 calls the op **Incline**, NOT Draft): `FaceArray()`
|
||||
(by index) + `SetPlane(neutral base plane)` + `angle` (degrees) + `direction`. **Empirical, opposite to
|
||||
the SDK docs:** `direction=false` = expanding/outward (adds material), `true` = tapering/inward → the code
|
||||
maps `def.direction = !outward`. Validate `0<angle<90`. (`o3d_DraftFromEdges=644` / `IDraftFromEdges` is
|
||||
a different op — not used.)
|
||||
- **Patterns & mirror (API5)**: `linear_pattern` (`ksMeshCopyDefinition`, =35): `SetAxis1(axis)` +
|
||||
`SetCopyParamAlongAxis(true,0,count,step,false)` + `count2=1` (disables the 2nd direction → 1D); features
|
||||
via `OperationArray()`. `circular_pattern` (`ksCircularCopyDefinition`, =36): `SetAxis(axis)`; `count1=1`
|
||||
(radial off), `count2=count` (ring), `step2=angle°` between neighbours, `factor2=false`, `inverce=reverse`;
|
||||
`GetOperationArray().Add(feature)`. `mirror_operation` (`ksMirrorCopyDefinition`, =48): `SetPlane(base)` +
|
||||
`GetOperationArray().Add(feature)`. `mirror_body` (`ksMirrorCopyAllDefinition`, =49): `SetPlane(base)`;
|
||||
`ChooseBodies()` casts to null in the interop and is NOT needed — it mirrors all bodies keeping the original
|
||||
(volume doubles). Empirical: `count` includes the original instance (count=3 → 3 instances). Axes:
|
||||
`CoordinateAxis{X,Y,Z}` → `o3d_axisOX/OY/OZ` (`Core/Modeling/CoordinateAxis.cs`, analogous to `BasePlane.cs`).
|
||||
- **Hole ops (API7 only — `ksHoleDefinition` is absent from the API5 interop; `HoleService`)**:
|
||||
`HoleCore(ksHoleTypeEnum holeType, Action<IHole3D> configure)` — `configure` sets `HoleParameters` **after**
|
||||
`HoleType` is assigned (the type-specific sub-interface is inaccessible until the type is fixed). Placement
|
||||
is shared: `IModelContainer`/`IHole3D`/`IHoleDisposal` + `Points3D`, direction chosen by volume delta,
|
||||
orphan rollback. None are registered in `_features` → no id returned. `hole`=`ksHTBase`, configure=null.
|
||||
`hole_counterbore`=`ksHTCounterbore` + `(ISpotfacingHoleParameters)` (`SpotfacingDiameter`/`SpotfacingDepth`).
|
||||
`hole_countersink`=`ksHTCountersinking` + `(ICountersinkHoleParameters)` (`CountersinkType=ksCTDiameterAngle`,
|
||||
`CountersinkDiameter`/`Angle`). `hole_conic`=`ksHTConic` + `(IConicHoleParameters)` (`ConicType=ksCNAngle`,
|
||||
`ConicAngle`).
|
||||
- **Assembly (API7, `AssemblyService`, namespace `Kompas.Mcp.Core.Assemblies`)**: `assembly_add_component` —
|
||||
`IParts7.AddFromFile(path, ExternalFile=true, Redraw=true)` → `IPart7.Placement` (`IPlacement3D`) →
|
||||
`SetOrigin(x,y,z)` (mm, assembly world CS) → `UpdatePlacement(true)` → `top.RebuildModel(true)`.
|
||||
**Empirical:** `UpdatePlacement` returns FALSE for a manually-positioned component (no mates) — NOT an error.
|
||||
Validate `DocumentType==ksDocumentAssembly` before touching `TopPart`; rollback via `IFeature7.Delete` +
|
||||
`RebuildModel`. `assembly_add_mate` — `top.MateConstraints` (`IMateConstraints3D`) →
|
||||
`Add(MateConstraintType)` → `BaseObject1/2` (faces via `top.FindObjectsByPoint(x,y,z,FirstLevel=false)`) →
|
||||
`ParamValue` (distance) → `Update()` → `RebuildModel(true)`; check `mate.Valid` (false → rollback
|
||||
`mate.Owner.Delete`). Types `coincidence` (mc_Coincidence=0) and `distance` (mc_Distance=5, value>0) are
|
||||
verified live; the rest are backlog.
|
||||
- **Drawing (API7, `DrawingService`, namespace `Kompas.Mcp.Core.Drawings`)** — entry via `IKompasDocument2D`;
|
||||
requires an active drawing document (`DocumentType==ksDocumentDrawing`). All coordinates are **view-local**
|
||||
(mm) unless noted. The model file must be saved before placing views.
|
||||
- `drawing_create_standard_views`: `doc2d.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди",
|
||||
projTypes, x, y, scale, dx=20, dy=20)` where `projTypes=object[]{1,3,5}` (Front/Up/Left as SAFEARRAY VT_I4).
|
||||
Success: `created==3`; content check: sum of the new views' `IView.ObjectCount` > 0 else rollback
|
||||
`IView.Delete`. Returns `ViewNumbers` (the `IView.Number` values — address views by these when placing
|
||||
annotations).
|
||||
- `drawing_fill_title_block`: `doc2d.LayoutSheets.ItemByNumber[1].Stamp` (`IStamp`) →
|
||||
`stamp.Text[columnId].Str = text` (indexed property; `IText.Str` overwrites, no Clear needed) →
|
||||
`stamp.Update()`. Fields→columns (`ksStampEnum`): Name=1, Designation=2, Material=3. Non-transactional
|
||||
(all cells → a single `Update`).
|
||||
- **Dimensions go into `(ISymbols2DContainer)view`, NOT the sheet, and are NOT counted in `IView.ObjectCount`**
|
||||
(that counts geometry in `IDrawingContainer`) — verify placement via the container's `*.Count`. Common flow:
|
||||
`Add()` → set coords → `AutoNominalValue=true` → `Update()` → check `Valid` → read
|
||||
`((IDimensionText)dim).NominalValue`; rollback `dim.Delete()` on FALSE/invalid/zero. Leader and angle are in
|
||||
**radians** (`DimensionAngles.ToRadians`). Free placement = set coordinates directly; **associative**
|
||||
(diametral/radial only) = set `BaseObject` to a projected circle → the value is read from geometry.
|
||||
- linear (`ILineDimension`): `X1,Y1,X2,Y2` (extension points) + `X3,Y3` (dimension line) + `Orientation`
|
||||
(`DimensionOrientation{Horizontal,Vertical,Parallel}` → `ksLinDParallel=0`/`Horizontal=1`/`Vertical=2`;
|
||||
parallel needs no explicit Angle).
|
||||
- diametral (`IDiametralDimension`): `Xc,Yc,Radius` + `Angle`; `NominalValue` = 2·Radius.
|
||||
- radial (`IRadialDimension`): `Xc,Yc,Radius` + `Angle`, `DimensionType=true`; **`NominalValue` = Radius, NOT diameter** — contrary to SDK docs, confirmed by spike.
|
||||
- **associative** diametral/radial (`associate=true` flag): instead of coords, find a projected circle in `(IDrawingContainer)view.Circles` by center+radius key (`CircularObjectMatch.SelectMatchIndex`, tol 1 mm; throws on none/ambiguous; concentric disambiguated by radius), set `dim.BaseObject = circle` (`_Circle` implements `IDrawingObject`) → `NominalValue` read from geometry. `RequireViewContainers` gives both QIs; free path stays on `RequireSymbols2DContainer`. Arc/angular/rough binding — not yet (own spike needed; `ILineDimension` has no `BaseObject`).
|
||||
- angular (`IAngleDimension`, `AngleDimensions.Add(ksDrADimension=10)`): vertex `Xc,Yc` + side pts `X1,Y1`/`X2,Y2` + arc-position `X3,Y3` (positions the arc only, does NOT pick the angle); measured angle chosen by `DimensionType` = `AngleDimensionType{Min,Max,More}` (on rays 0°/45° → min=45°/max=135° supplement/more=315° reflex). `NominalValue` in degrees.
|
||||
- `drawing_add_rough` (`Roughs.Add()`/`IRough`: `BranchX0/Y0`, `Angle`) + `(IRoughParams)rough` QI: `SignType` = `RoughSignType{NoProcessing,DeleteMaterial,WithoutDeleteMaterial}`, value (Ra/Rz) = `RoughParamText.Str`.
|
||||
- `drawing_add_text` — **text lives in `IDrawingContainer`, NOT `ISymbols2DContainer`, and NOT in `IView.ObjectCount`**: `(IDrawingContainer)view.DrawingTexts.Add()`/`IDrawingText` (`X/Y/Angle`) + `(IText)dt.Str` = text; count via `DrawingTexts.Count`. Do NOT set `Height` (that's block height, not font).
|
||||
- `drawing_set_technical_requirements` — **document-level**: `(IDrawingDocument)doc.TechnicalDemand`/`ITechnicalDemand`: `td.Text.Str = text` → `td.Update()` (`IsCreated` False→True on first set; overwrites; `\n` multiline).
|
||||
- `DrawingAnnotationResult{Value(read-back), ViewNumber}` is the shared result for rough/text.
|
||||
- `drawing_add_leader` — `ISymbols2DContainer.Leaders.Add(ksDrLeader)` → `IBaseLeader`. **КРИТИЧЕСКИЙ ПОРЯДОК (спайк):** новая выноска имеет 0 ответвлений → `(IBranchs)bl.AddBranchByPoint(0,x,y)` (остриё) ДО `SetBranchTextPosition(textX,textY)`/`Update`, иначе КОМПАС падает `RPC_E_SERVERFAULT`. Текст — `(ILeader)bl.TextOnShelf.Str`; `ShelfDirection` (enum `ShelfDirection{Auto,Right,Left,Up,Down}`, Auto = не задавать). `AddBranchByPoint`/`SetBranchTextPosition` возвращают bool — проверять. Результат — общий `DrawingAnnotationResult`.
|
||||
- `drawing_set_sheet_format` — `RequireLayoutSheet(n).Format` (`ISheetFormat`): `Format`=`ksDocumentFormatEnum` (`PaperFormat{A0..A5,User}`+`PaperFormats.Parse/ToKompas/FromKompas`), standard → set `VerticalOrientation=!landscape` (W/H auto-computed from enum+orientation, e.g. A3 landscape→420×297); **User → set `FormatWidth/Height` only, КОМПАС auto-derives `VerticalOrientation` from W/H and ignores the flag, never swaps W/H (spike)** → `sheet.Update()`. New drawing defaults A4 portrait. `ValidateFormatDimensions`: User needs W/H>0, standard forbids W/H (must be 0).
|
||||
- **Richer sketch primitives (API5 `ksDocument2D` wrappers)**: `ksArcBy3Points`; `ksArcByAngle` (centre/radius/start-end angles in degrees + counterClockwise flag); `ksEllipse` via `ksEllipseParam` (`KompasObject.GetParamStruct(ko_EllipseParam=22)`; **property names are `A`/`B` UPPERCASE in the interop**); `ksRegularPolygon` via `ksRegularPolygonParam` (`ko_RegularPolygonParam=92`; `describe = !inscribed`); NURBS spline via `ksNurbs(order=4)` + `ksNurbsPoint` loop + `ksEndObj`; `ksPoint`. Param structs released after use. `PartModeler` is partial: `.cs` (registry/helpers/`NewParam<T>`/reset), `.Sketch.cs` (2D primitives), `.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` (`Core/Modeling`) holds enum maps + validators (`RequirePositive`, `RequireVertexCount`, `RequirePoints`, `RequireMin`). Point lists use `record SketchPoint(X, Y)` (JSON names `x`/`y`).
|
||||
- radial (`IRadialDimension`): `Xc,Yc,Radius` + `Angle`, `DimensionType=true`; **`NominalValue` = Radius,
|
||||
NOT diameter** — contrary to the SDK docs, confirmed by a spike.
|
||||
- **associative** diametral/radial (`associate=true`): instead of coordinates, find a projected circle in
|
||||
`(IDrawingContainer)view.Circles` by a center+radius key (`CircularObjectMatch.SelectMatchIndex`, tol 1 mm;
|
||||
throws on none/ambiguous; concentric disambiguated by radius), set `dim.BaseObject = circle` (`_Circle`
|
||||
implements `IDrawingObject`) → `NominalValue` read from geometry. `RequireViewContainers` gives both QIs;
|
||||
the free path stays on `RequireSymbols2DContainer`. Arc/angular/rough binding — not yet (needs its own
|
||||
spike; `ILineDimension` has no `BaseObject`).
|
||||
- angular (`IAngleDimension`, `AngleDimensions.Add(ksDrADimension=10)`): vertex `Xc,Yc` + side points
|
||||
`X1,Y1`/`X2,Y2` + arc-position `X3,Y3` (positions the arc only, does NOT pick the angle); the measured
|
||||
angle is chosen by `DimensionType` = `AngleDimensionType{Min,Max,More}` (on rays 0°/45° → min=45° /
|
||||
max=135° supplement / more=315° reflex). `NominalValue` in degrees.
|
||||
- `drawing_add_rough` (`Roughs.Add()`/`IRough`: `BranchX0/Y0`, `Angle`) + `(IRoughParams)rough` QI:
|
||||
`SignType` = `RoughSignType{NoProcessing,DeleteMaterial,WithoutDeleteMaterial}`, value (Ra/Rz) =
|
||||
`RoughParamText.Str`.
|
||||
- `drawing_add_text` — **text lives in `IDrawingContainer`, NOT `ISymbols2DContainer`, and NOT in
|
||||
`IView.ObjectCount`**: `(IDrawingContainer)view.DrawingTexts.Add()`/`IDrawingText` (`X/Y/Angle`) +
|
||||
`(IText)dt.Str` = text; count via `DrawingTexts.Count`. Do NOT set `Height` (that's block height, not font).
|
||||
- `drawing_set_technical_requirements` — **document-level**: `(IDrawingDocument)doc.TechnicalDemand` /
|
||||
`ITechnicalDemand`: `td.Text.Str = text` → `td.Update()` (`IsCreated` False→True on first set; overwrites;
|
||||
`\n` multiline).
|
||||
- `drawing_add_leader` — `ISymbols2DContainer.Leaders.Add(ksDrLeader)` → `IBaseLeader`. **Critical order
|
||||
(spike):** a fresh leader has 0 branches → `(IBranchs)bl.AddBranchByPoint(0,x,y)` (the tip) BEFORE
|
||||
`SetBranchTextPosition(textX,textY)`/`Update`, otherwise КОМПАС dies with `RPC_E_SERVERFAULT`. Text —
|
||||
`(ILeader)bl.TextOnShelf.Str`; `ShelfDirection` (enum `ShelfDirection{Auto,Right,Left,Up,Down}`, Auto = leave
|
||||
unset). `AddBranchByPoint`/`SetBranchTextPosition` return bool — check them.
|
||||
- `drawing_set_sheet_format` — `RequireLayoutSheet(n).Format` (`ISheetFormat`): `Format`=`ksDocumentFormatEnum`
|
||||
(`PaperFormat{A0..A5,User}` + `PaperFormats.Parse/ToKompas/FromKompas`); standard → set
|
||||
`VerticalOrientation=!landscape` (W/H auto-computed from enum+orientation, e.g. A3 landscape → 420×297);
|
||||
**User → set `FormatWidth/Height` only, КОМПАС derives `VerticalOrientation` from W/H, ignores the flag and
|
||||
never swaps W/H (spike)** → `sheet.Update()`. A new drawing defaults to A4 portrait.
|
||||
`ValidateFormatDimensions`: User needs W/H>0, standard forbids W/H (must be 0).
|
||||
- `DrawingAnnotationResult{Value(read-back), ViewNumber}` is the shared result for rough/text/leader.
|
||||
- **Richer sketch primitives (API5 `ksDocument2D` wrappers)**: `ksArcBy3Points`; `ksArcByAngle` (centre/radius/
|
||||
start-end angles in degrees + counterClockwise flag); `ksEllipse` via `ksEllipseParam`
|
||||
(`KompasObject.GetParamStruct(ko_EllipseParam=22)`; **the property names are `A`/`B` UPPERCASE in the interop**);
|
||||
`ksRegularPolygon` via `ksRegularPolygonParam` (`ko_RegularPolygonParam=92`; `describe = !inscribed`); a NURBS
|
||||
spline via `ksNurbs(order=4)` + `ksNurbsPoint` loop + `ksEndObj`; `ksPoint`. Param structs are released after
|
||||
use. `PartModeler` is partial: `.cs` (registry/helpers/`NewParam<T>`/reset), `.Sketch.cs` (2D primitives),
|
||||
`.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` (`Core/Modeling`) holds the enum maps
|
||||
+ validators (`RequirePositive`, `RequireVertexCount`, `RequirePoints`, `RequireMin`). Point lists use
|
||||
`record SketchPoint(X, Y)` (JSON names `x`/`y`).
|
||||
|
||||
## КОМПАС-3D API architecture (the critical context)
|
||||
|
||||
КОМПАС-3D is automated via a **COM Automation API**, Windows-only. КОМПАС-3D **must be installed and running/launchable** on the same machine — the MCP server is a COM client, not a standalone CAD engine. There are **two coexisting API generations**:
|
||||
КОМПАС-3D is automated via a **COM Automation API**, Windows-only. КОМПАС **must be installed and
|
||||
running/launchable on the same machine** — the MCP server is a COM client, not a standalone CAD engine.
|
||||
Two API generations coexist:
|
||||
|
||||
| | Entry point | Namespace (C#) | Style | Use when |
|
||||
|---|---|---|---|---|
|
||||
| **API5** (legacy) | `KompasObject` | `Kompas6API5` | procedural, flat | older features, 2D primitives, bootstrapping |
|
||||
| **API7** (modern, preferred) | `IApplication` / `_Application` | `KompasAPI7` | OOP, interface-based | everything new — documents, 3D, parameters |
|
||||
| **API5** (legacy) | `KompasObject` | `Kompas6API5` | procedural, flat | 3D `ksPart` building, 2D primitives, bootstrapping |
|
||||
| **API7** (modern, preferred) | `IApplication` / `_Application` | `KompasAPI7` | OOP, interface-based | documents, holes, assemblies, drawings, B-rep edit |
|
||||
|
||||
Constants live in separate libraries: `Kompas6Constants`, `Kompas6Constants3D` (C#), or `ksConstants.h` / `ksConstants3D.h` (C++).
|
||||
Constants live in separate libraries: `Kompas6Constants`, `Kompas6Constants3D` (C#), or `ksConstants.h` /
|
||||
`ksConstants3D.h` (C++).
|
||||
|
||||
### Connection pattern (verified from `Samples/CSharp.zip`)
|
||||
|
||||
1. Get the КОМПАС root object by COM ProgID. **Verified on this machine (v24 Home):**
|
||||
`KOMPAS.Application.7` and `KOMPAS.Application.5` are registered; `KOMPASLT.*` is **absent** —
|
||||
the Home edition uses the regular (non-LT) ProgIDs. Resolution order:
|
||||
`KOMPAS.Application.7` and `KOMPAS.Application.5` are registered; `KOMPASLT.*` is **absent** — the Home
|
||||
edition uses the regular (non-LT) ProgIDs. Resolution order:
|
||||
- `KOMPAS.Application.7` — direct API7 `IApplication` (preferred)
|
||||
- `KOMPAS.Application.5` — API5 `KompasObject`, then `ksGetApplication7()`
|
||||
- `KOMPASLT.Application.5` — fallback for other installations
|
||||
- New instance: `Activator.CreateInstance(Type.GetTypeFromProgID(progId))`
|
||||
- Attach to a running instance: `Marshal.GetActiveObject(progId)`
|
||||
2. If you entered via API5, obtain the modern application: `IApplication appl = (IApplication)kompas.ksGetApplication7();` (via `KOMPAS.Application.7` you already hold `IApplication`).
|
||||
3. Set `appl.Visible = true` to show the КОМПАС window; drive documents via API7 interfaces (`IKompasDocument2D`, `IKompasDocument3D`, …).
|
||||
2. If you entered via API5, obtain the modern application:
|
||||
`IApplication appl = (IApplication)kompas.ksGetApplication7();`
|
||||
3. Set `appl.Visible = true` to show the КОМПАС window; drive documents via API7 interfaces
|
||||
(`IKompasDocument2D`, `IKompasDocument3D`, …).
|
||||
|
||||
> The SDK C# samples are mostly **КОМПАС plugin libraries** (DLLs loaded *into* КОМПАС as ActiveX, entry methods like `ExternalRunCommand`/`ExternalMenuItem`, registered via `[ComRegisterFunction]`). For an MCP server we want the **opposite direction**: a standalone *external automation client* that connects out-of-process via the ProgID pattern above. Read the samples for API usage, not for the plugin packaging/registration boilerplate.
|
||||
> The SDK C# samples are mostly **КОМПАС plugin libraries** (DLLs loaded *into* КОМПАС as ActiveX, entry
|
||||
> methods like `ExternalRunCommand`/`ExternalMenuItem`, registered via `[ComRegisterFunction]`). This server
|
||||
> is the **opposite direction**: a standalone *external automation client* connecting out-of-process via the
|
||||
> ProgID pattern above. Read the samples for API usage, not for the plugin packaging/registration boilerplate.
|
||||
|
||||
## SDK layout (read-only reference, not part of this repo)
|
||||
|
||||
Under `C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK`:
|
||||
- `lib\*.tlb` — COM type libraries to reference / generate interop from: `kAPI5.tlb`, `kAPI7.tlb`, `ksConstants.tlb`, `ksConstants3D.tlb`, `kAPI2D5COM.tlb`, `kAPI3D5COM.tlb`.
|
||||
- `lib\*.tlb` — COM type libraries: `kAPI5.tlb`, `kAPI7.tlb`, `ksConstants.tlb`, `ksConstants3D.tlb`,
|
||||
`kAPI2D5COM.tlb`, `kAPI3D5COM.tlb`.
|
||||
- `lib64\` — 64-bit import libs (`kAPI7.lib`, `kAPI5.lib`, …) and `.a` for C++/Builder.
|
||||
- `Include\` — C++/Pascal headers (`Ks_TLB.h`, `kAPI2D5COM.h`, `LDefin2D.pas`, …).
|
||||
- `KsAPI\` — the **KsAPI** (Qt/C++ cross-platform flavour): `Include\KsAPI.h`, `ksConstants*.h`, `Lib64\ksAPI.lib`, plus `Help\С чего начать.pdf` and a Linux/Debian 12 build guide.
|
||||
- `Samples\` — zipped examples per language: `CSharp.zip`, `C++.zip`, `Pascal.zip`, `Basic.zip`. Inside `CSharp.zip`, the `Step1`…`Step12` and `Step2_API7_2D` / `Step2_API7_3D` projects are the progressive tutorial; `Gayka`, `SlideWrk`, `EventsAuto` are larger feature demos.
|
||||
- `Help\KOMPAS_SDK_ru-RU.zip` and `KsAPI\Help\KsAPI_Help.zip` — offline copy of the docs.
|
||||
- `KsAPI\` — the **KsAPI** (Qt/C++ cross-platform flavour): `Include\KsAPI.h`, `ksConstants*.h`,
|
||||
`Lib64\ksAPI.lib`, plus a Linux/Debian 12 build guide. Not used by this project.
|
||||
- `Samples\` — zipped examples per language: `CSharp.zip`, `C++.zip`, `Pascal.zip`, `Basic.zip`. Inside
|
||||
`CSharp.zip` the `Step1`…`Step12` and `Step2_API7_2D`/`Step2_API7_3D` projects are the progressive tutorial;
|
||||
`Gayka`, `SlideWrk`, `EventsAuto` are larger feature demos. `Common/` holds the interop assemblies the
|
||||
server extracts at runtime.
|
||||
- `Help\KOMPAS_SDK_ru-RU.zip` — the offline docs the RAG base is built from.
|
||||
|
||||
To inspect a sample without unpacking the whole archive: `unzip -p "<path>\Samples\CSharp.zip" "Automation/Step2_API7_3D/Step2_API7_3D.cs"`. Note the `.cs` sample sources are **Windows-1251 encoded** (Cyrillic comments appear garbled in UTF-8 tools).
|
||||
To inspect a sample without unpacking the whole archive:
|
||||
`unzip -p "<path>\Samples\CSharp.zip" "Automation/Step2_API7_3D/Step2_API7_3D.cs"`. The `.cs` sample sources
|
||||
are **Windows-1251 encoded** (Cyrillic comments appear garbled in UTF-8 tools).
|
||||
|
||||
## Conventions / gotchas
|
||||
|
||||
- **Windows-only & 64-bit**: target x64 to match the installed КОМПАС; mixing bitness breaks COM activation. КОМПАС v24 Home is the installed edition — some full-edition API features may be unavailable.
|
||||
- **Encoding**: SDK source samples are CP1251. New project files should be UTF-8.
|
||||
- **COM lifetime**: release COM objects (`Marshal.ReleaseComObject`) / scope them; a leaked reference keeps the КОМПАС process alive.
|
||||
- **API choice**: prefer API7 for new tool implementations; drop to API5 only for things API7 doesn't expose.
|
||||
- **Windows-only & 64-bit**: target x64 to match the installed КОМПАС; mixing bitness breaks COM activation.
|
||||
v24 Home is the installed edition — some full-edition API features may be unavailable.
|
||||
- **Encoding**: SDK sources are CP1251; project files are UTF-8; plugin PowerShell is UTF-8 **with BOM**.
|
||||
- **COM lifetime**: release COM objects (`Marshal.ReleaseComObject`) or scope them; a leaked reference keeps
|
||||
the КОМПАС process alive.
|
||||
- **API choice**: prefer API7 for new tools; drop to API5 only for what API7 doesn't expose.
|
||||
- **Sequential tool calls**: the build session keeps sketches by `id` and the server serves requests
|
||||
concurrently — dependent tool calls must be awaited one at a time.
|
||||
|
||||
@@ -2,36 +2,43 @@
|
||||
|
||||
**MCP-сервер для управления CAD-системой КОМПАС-3D (АСКОН) языковой моделью.**
|
||||
|
||||
Сервер — это COM-клиент КОМПАС-3D: он подключается к установленному КОМПАС и предоставляет
|
||||
LLM набор инструментов для создания документов, построения 3D-деталей (эскиз → выдавливание),
|
||||
импорта/экспорта STEP, работы со сборками, структурного осмотра модели (`describe_model` — дерево
|
||||
операций, тела, переменные, топология одним вызовом) и снимка модели (агент «видит» результат).
|
||||
Сервер — COM-клиент КОМПАС-3D: подключается к установленному КОМПАС и даёт LLM 84 инструмента для
|
||||
создания документов, построения 3D-деталей (эскиз → формообразующая операция), работы со сборками и
|
||||
чертежами, импорта/экспорта STEP, прямого редактирования импортированной B-rep, а также структурного
|
||||
осмотра модели (`describe_model` — дерево операций, тела, топология, переменные, МЦХ одним вызовом)
|
||||
и снимка модели, по которому агент «видит» результат.
|
||||
|
||||
Принцип: **MCP транслирует возможности SDK КОМПАС в общие инструменты** (не под конкретную задачу).
|
||||
Поверх MCP — навык **`.claude/skills/kompas-3d/`** с методикой работы (playbook'и, эвристики,
|
||||
обкатанные в `usecases/`). Отдельный навык **`.claude/skills/kompas-fdm-design/`** — методика
|
||||
проектирования под FDM/FFF 3D-печать (правила DFM + лёгкий гео-аудит инструментами осмотра);
|
||||
работает поверх `kompas-3d`, экспорт через `export_step`, слайсер не вызывает.
|
||||
Принцип: **MCP транслирует возможности SDK КОМПАС в общие инструменты**, а не под конкретную задачу.
|
||||
Поверх MCP — навык **`kompas-3d`** с методикой работы (playbook'и, эвристики). Отдельный навык
|
||||
**`kompas-fdm-design`** — методика проектирования под FDM/FFF 3D-печать (правила DFM + лёгкий
|
||||
гео-аудит инструментами осмотра): работает поверх `kompas-3d`, экспортирует через `export_step`,
|
||||
слайсер не вызывает.
|
||||
|
||||
> Подробности: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) · план — [docs/IMPLEMENTATION_PLAN.md](docs/IMPLEMENTATION_PLAN.md)
|
||||
> · спорные вопросы — [docs/OPEN_QUESTIONS.md](docs/OPEN_QUESTIONS.md) · обзор — [docs/presentation.html](docs/presentation.html)
|
||||
Проектной документации в отдельном каталоге нет: этот README — обзор и каталог инструментов,
|
||||
[CLAUDE.md](CLAUDE.md) — архитектура, проверенные COM-приёмы и подводные камни.
|
||||
|
||||
## Требования
|
||||
|
||||
- **Windows x64**, установленный **КОМПАС-3D** (разрабатывалось на v24 Home) **вместе с компонентом
|
||||
SDK**: interop-сборки в поставку сервера не входят и берутся из установки —
|
||||
`SDK\Samples\CSharp.zip` (каталог `Common`). Если SDK не установлен, положите сборки рядом с
|
||||
SDK**: interop-сборки принадлежат АСКОН, в поставку сервера не входят и берутся из установки —
|
||||
`SDK\Samples\CSharp.zip` (каталог `Common`), при первом запуске распаковываются в
|
||||
`%LOCALAPPDATA%\kompas-mcp\interop\<ключ>`. Если SDK не установлен, положите сборки рядом с
|
||||
`kompas-mcp.exe` или укажите каталог с ними в `KOMPAS_INTEROP_DIR`.
|
||||
- **.NET SDK 8+** (собирается и на SDK 10; целевой фреймворк `net8.0-windows`).
|
||||
- **.NET SDK 8+** для сборки из исходников (целевой фреймворк `net8.0-windows`; формат решения
|
||||
`.slnx` понимает только SDK 9+ — на SDK 8 собирайте проекты по путям). Для готового релиза рантайм
|
||||
не нужен: сервер публикуется self-contained.
|
||||
|
||||
## Установка как плагин Claude Code
|
||||
|
||||
Сервер и оба навыка (`kompas-3d`, `kompas-fdm-design`) упакованы в плагин `kompas` (каталог
|
||||
[`plugin/`](plugin/)) — это самый простой способ подключить проект к Claude Code без ручной сборки:
|
||||
установка через `/plugin marketplace add` + `/plugin install`, лаунчер сам скачивает и проверяет
|
||||
self-contained сборку сервера по `plugin/server.lock.json`. Подробности, режим разработки
|
||||
(`KOMPAS_MCP_EXE`) и сниппеты для Codex/opencode — в [`plugin/README.md`](plugin/README.md).
|
||||
Публичного релиза пока нет — используйте режим разработки или сборку ниже.
|
||||
Сервер и оба публикуемых навыка упакованы в плагин `kompas` (каталог [`plugin/`](plugin/)) — самый
|
||||
простой способ подключить проект к Claude Code без ручной сборки: `/plugin marketplace add` +
|
||||
`/plugin install`, дальше лаунчер сам скачивает self-contained сборку сервера по
|
||||
`plugin/server.lock.json` и сверяет её SHA256. Проверка установки — команда `/kompas:doctor`.
|
||||
Подробности, режим разработки (`KOMPAS_MCP_EXE`) и сниппеты для Codex/opencode — в
|
||||
[`plugin/README.md`](plugin/README.md).
|
||||
|
||||
> Публичного релиза пока нет (`server.lock.json` в состоянии `0.0.0`, запись в каталоге плагинов не
|
||||
> заведена) — используйте режим разработки или сборку ниже.
|
||||
|
||||
## Сборка и запуск
|
||||
|
||||
@@ -41,6 +48,7 @@ dotnet build -c Release
|
||||
dotnet run --project src/Kompas.Mcp.Host -c Release
|
||||
# или собранный exe (платформа x64 — из Directory.Build.props):
|
||||
src/Kompas.Mcp.Host/bin/x64/Release/net8.0-windows/kompas-mcp.exe
|
||||
kompas-mcp.exe --version # версия бинаря, КОМПАС для этого не нужен
|
||||
```
|
||||
|
||||
### Подключение к MCP-клиенту (stdio)
|
||||
@@ -48,12 +56,12 @@ src/Kompas.Mcp.Host/bin/x64/Release/net8.0-windows/kompas-mcp.exe
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"kompas3d": { "command": "C:\\путь\\к\\kompas-mcp.exe", "args": [] }
|
||||
"kompas": { "command": "C:\\путь\\к\\kompas-mcp.exe", "args": [] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Инструменты (84 инструмента)
|
||||
## Инструменты (84)
|
||||
|
||||
| Группа | Инструменты |
|
||||
|---|---|
|
||||
@@ -63,12 +71,12 @@ src/Kompas.Mcp.Host/bin/x64/Release/net8.0-windows/kompas-mcp.exe
|
||||
| Features | `extrude_boss`, `extrude_cut`, `revolve_boss`, `revolve_cut`, `fillet_edge`, `chamfer_edge`, `fillet_edge_index`, `chamfer_edge_index`, `shell`, `rib`, `sweep`, `loft`, `linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body`, `hole`, `hole_counterbore`, `hole_countersink`, `hole_conic`, `draft`, `rebuild` |
|
||||
| Edit | `move_face`, `split_solid_by_plane`, `move_body`, `boolean_union` |
|
||||
| Inspection | `describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure` |
|
||||
| Vision | `model_snapshot` (PNG-снимок; fallback — сначала `describe_model`) |
|
||||
| Vision | `model_snapshot` (PNG-снимок; сначала — `describe_model`) |
|
||||
| Query | `get_part_info`, `get_bounding_box`, `list_faces`, `list_edges`, `list_components` |
|
||||
| Variables | `create_variable`, `set_variable`, `set_variable_note`, `delete_variable` |
|
||||
| Conversion | `import_step`, `export_step` |
|
||||
| Assembly | `assembly_add_component`, `assembly_add_mate` |
|
||||
| Drawing | `drawing_create_standard_views`, `drawing_fill_title_block`, `drawing_add_linear_dimension`, `drawing_add_diametral_dimension`, `drawing_add_radial_dimension`, `drawing_add_angular_dimension`, `drawing_add_rough`, `drawing_add_text`, `drawing_set_technical_requirements`, `drawing_set_sheet_format`, `drawing_add_leader` |
|
||||
| Drawing | `drawing_create_standard_views`, `drawing_fill_title_block`, `drawing_add_linear_dimension`, `drawing_add_diametral_dimension`, `drawing_add_radial_dimension`, `drawing_add_angular_dimension`, `drawing_add_leader`, `drawing_add_rough`, `drawing_add_text`, `drawing_set_technical_requirements`, `drawing_set_sheet_format` |
|
||||
| Validation | `validate_part` |
|
||||
|
||||
### Типовой сценарий
|
||||
@@ -79,29 +87,29 @@ src/Kompas.Mcp.Host/bin/x64/Release/net8.0-windows/kompas-mcp.exe
|
||||
> `describe_model` предпочтителен перед `model_snapshot`: отдаёт структурный «паспорт» детали
|
||||
> (дерево операций, тела, переменные, топологию, МЦХ) без расхода токенов на изображение.
|
||||
|
||||
> ⚠️ Вызовы зависимых инструментов выполняйте **последовательно** (дождавшись ответа):
|
||||
> сессия построения хранит эскизы по `id`, а сервер обрабатывает запросы конкурентно.
|
||||
> ⚠️ Вызовы зависимых инструментов выполняйте **последовательно**, дождавшись ответа: сессия
|
||||
> построения хранит эскизы по `id`, а сервер обрабатывает запросы конкурентно.
|
||||
|
||||
## Структура
|
||||
|
||||
```
|
||||
src/Kompas.Mcp.Core/ COM-слой: STA-диспетчер, подключение, документы, эскизы/операции, конвертация, снимок, инспекция модели
|
||||
src/Kompas.Mcp.Host/ MCP-сервер (stdio) + определения инструментов
|
||||
tests/Kompas.Mcp.Tests/ 381 тест: unit + integration (integration требуют КОМПАС)
|
||||
libs/kompas-interop/ interop-сборки КОМПАС (из SDK Samples/Common) — только для компиляции, в поставку не входят
|
||||
docs/ архитектура, план, презентация, спеки и планы (superpowers/)
|
||||
usecases/ полигон обкатки подходов (в .gitignore); приёмы поднимаются в навык kompas-3d
|
||||
plugin/ плагин Claude Code `kompas`: манифест, лаунчер, лок версии сервера, навыки (источник истины)
|
||||
tools/ скрипты раскладки навыков (junction) и их Pester-тесты
|
||||
.claude/skills/ junction на plugin/skills/{kompas-3d,kompas-fdm-design} + kompas-mcp-dev (внутренний навык)
|
||||
.claude/agents/ субагенты-делегаты: docs-maintainer (Sonnet), kompas-sdk-research (Haiku)
|
||||
src/Kompas.Mcp.Core/ COM-слой: STA-диспетчер, подключение, документы, эскизы/операции,
|
||||
сборки, чертежи, конвертация, снимок, инспекция модели
|
||||
src/Kompas.Mcp.Host/ MCP-сервер (stdio) + определения инструментов (Tools/ — по группе на файл)
|
||||
tests/Kompas.Mcp.Tests/ 381 тест: unit (корень) + Integration/ (нужен запущенный КОМПАС)
|
||||
libs/kompas-interop/ interop-сборки КОМПАС (из SDK Samples/Common) — только для компиляции
|
||||
plugin/ плагин Claude Code `kompas`: манифест, лаунчер, лок версии сервера,
|
||||
навыки kompas-3d и kompas-fdm-design (источник истины)
|
||||
tools/ раскладка навыков junction'ами + Pester-тесты PowerShell плагина
|
||||
.gitea/workflows/ CI (сборка + unit-тесты) и релиз (публикация в ветку dist)
|
||||
.claude/skills/ junction'ы на plugin/skills + kompas-mcp-dev (внутренний навык разработки)
|
||||
.claude/agents/ субагент kompas-sdk-research (Haiku) — поиск по справке SDK
|
||||
```
|
||||
|
||||
Справка по COM API КОМПАС живёт **вне репозитория** — в приватном
|
||||
[`kompas-sdk-docs`](https://git.shahovalov.ru/mikhail/kompas-sdk-docs) (2465 статей,
|
||||
собраны из HTML-справки SDK) и проиндексирована в RAG на CT 127. Доступ — через MCP-сервер
|
||||
`kompas-sdk` (порт 8092, bearer в `KOMPAS_SDK_RAG_TOKEN`); ищет по ней субагент
|
||||
`kompas-sdk-research`.
|
||||
[`kompas-sdk-docs`](https://git.shahovalov.ru/mikhail/kompas-sdk-docs) (2465 статей, собраны из
|
||||
HTML-справки SDK) и проиндексирована в RAG. Доступ — через MCP-сервер `kompas-sdk` (bearer в
|
||||
`KOMPAS_SDK_RAG_TOKEN`); ищет по ней субагент `kompas-sdk-research`.
|
||||
|
||||
## Тесты
|
||||
|
||||
@@ -109,28 +117,32 @@ tools/ скрипты раскладки навыков (junctio
|
||||
dotnet test # все (integration требуют КОМПАС)
|
||||
dotnet test --filter "Category=Unit" # только unit (без COM)
|
||||
dotnet test --filter "Category=Integration" # только integration
|
||||
pwsh -NoProfile -File tools/tests/run-ps-tests.ps1 # Pester-тесты скриптов плагина
|
||||
```
|
||||
|
||||
381 тест: 250 unit + 131 integration. CI (Gitea Actions, `.gitea/workflows/ci.yml`) собирает и
|
||||
гоняет `Category=Unit&Requires!=Windows` на Linux-раннере (245 тестов) — 5 тестов диспетчера STA
|
||||
помечены `Requires=Windows` (`Thread.SetApartmentState` не работает на Linux) и в CI не идут;
|
||||
integration-тесты в CI не запускаются никогда (нужен запущенный КОМПАС с GUI и лицензией).
|
||||
Плюс 41 Pester-тест PowerShell-скриптов плагина и раскладки навыков (`tools/tests/run-ps-tests.ps1`)
|
||||
— в счёт .NET-тестов не входят.
|
||||
381 тест: 250 unit + 131 integration. CI (Gitea Actions, `.gitea/workflows/ci.yml`) собирает проект и
|
||||
гоняет `Category=Unit&Requires!=Windows` на Linux-раннере (245 тестов) — 5 тестов STA-диспетчера
|
||||
помечены `Requires=Windows` (`Thread.SetApartmentState` не работает на Linux). Integration-тесты в CI
|
||||
не запускаются никогда: нужен запущенный КОМПАС с GUI и лицензией. Плюс 41 Pester-тест
|
||||
PowerShell-скриптов плагина и раскладки навыков — в счёт .NET-тестов не входят.
|
||||
|
||||
## Архитектура (кратко)
|
||||
|
||||
- **STA-поток** (`KompasDispatcher`) владеет всеми COM-вызовами — КОМПАС однопоточный STA.
|
||||
- Подключение через API5 `KompasObject` (`KOMPAS.Application.5`) → API7 `IApplication` (`ksGetApplication7`).
|
||||
- **Interop-сборки подставляются в рантайме** (`KompasInteropLoader`) из установленного КОМПАС, а не из поставки сервера: установка находится по `HKCR\CLSID\{...}\LocalServer32`, сборки распаковываются из `SDK\Samples\CSharp.zip` в `%LOCALAPPDATA%\kompas-mcp\interop\<ключ>`.
|
||||
- 3D строится через API5 `ksPart`, снимок — через `ksDocument3D`; прямое редактирование импортированной B-rep — через API7. Структурный осмотр, переменные, сборки и чертежи инкапсулированы в сервисах COM-слоя.
|
||||
- STEP импорт/экспорт — через встроенный конвертер КОМПАС.
|
||||
- MCP — официальный C# SDK `ModelContextProtocol`, транспорт stdio, логи в stderr.
|
||||
- Детали реализации, обоснования и проверенные COM-приёмы — в [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) и [CLAUDE.md](CLAUDE.md).
|
||||
- Подключение через API5 `KompasObject` (`KOMPAS.Application.5`) → API7 `IApplication`
|
||||
(`ksGetApplication7`). 3D строится через API5 `ksPart`, снимок — через `ksDocument3D`; отверстия,
|
||||
сборки, чертежи и прямое редактирование B-rep — через API7.
|
||||
- **Interop-сборки подставляются в рантайме** (`KompasInteropLoader`) из установленного КОМПАС, а не
|
||||
из поставки сервера: установка находится по `HKCR\CLSID\{...}\LocalServer32`, сборки распаковываются
|
||||
из `SDK\Samples\CSharp.zip` в `%LOCALAPPDATA%\kompas-mcp\interop\<ключ>`.
|
||||
- STEP импорт/экспорт — через встроенный конвертер КОМПАС (AP203/AP214/AP242).
|
||||
- MCP — официальный C# SDK `ModelContextProtocol`, транспорт stdio, логи в stderr (stdout занят
|
||||
протоколом).
|
||||
- Детали реализации, обоснования и проверенные COM-приёмы — в [CLAUDE.md](CLAUDE.md).
|
||||
|
||||
## Лицензирование
|
||||
|
||||
`libs/kompas-interop/*.dll` — interop-сборки АСКОН из состава SDK КОМПАС-3D. Они нужны **только для
|
||||
компиляции** и в релизный архив сервера не попадают (`Private=false` в `Kompas.Mcp.Core.csproj`):
|
||||
сервер подставляет их в рантайме из установленного у пользователя КОМПАС-3D. Для работы требуется
|
||||
установленный КОМПАС-3D с компонентом SDK.
|
||||
компиляции** и в релизный архив сервера не попадают (`Private=false`): сервер подставляет их в
|
||||
рантайме из установленного у пользователя КОМПАС-3D. Для работы требуется установленный КОМПАС-3D
|
||||
с компонентом SDK.
|
||||
|
||||
Reference in New Issue
Block a user