e8b22c04e7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
108 lines
18 KiB
Markdown
108 lines
18 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## 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.
|
|
|
|
Authoritative references:
|
|
- **SDK knowledge base in this repo: `docs/Kompas3D_SDK/`** — clean Markdown, one article per COM interface, with YAML tags; search it with Grep/Read (see below). Committed to the repo (canonical), distilled from the КОМПАС SDK help. Prefer this over the web. For non-trivial lookups, delegate to the **`kompas-sdk-research`** skill (Sonnet subagent) to save context.
|
|
- 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`
|
|
|
|
## Navigating the SDK docs (`docs/Kompas3D_SDK/`)
|
|
|
|
`docs/Kompas3D_SDK/` is a structured MD knowledge base distilled from the 26 000-page Help&Manual WebHelp export. One article per COM interface/topic, split into `interfaces/`, `enums/`, `structures/`, `guides/` (+ `resources/` images), each with YAML frontmatter (`type`, `api`, `domain`, `tags`, `sources`); methods/properties are `## ` headings inside the interface article. **Search it directly — do not browse the raw HTML:**
|
|
|
|
- **Grep** over `docs/Kompas3D_SDK/` for an interface/method/property name or Russian keyword (method names are `## SetSideParam - …` headings, so the name lands you in the right article).
|
|
- **Read** the matching `*.md` — the interface article holds all its members, `Синтаксис Automation:`/`Синтаксис COM:` blocks (in code fences), parameters, notes.
|
|
- `docs/Kompas3D_SDK/index.md` — table of contents by category. Filter by frontmatter tags, e.g. `Grep "type: enum"` or `Grep "domain: \[.*3d"`.
|
|
|
|
The base is committed to the repo (canonical) — no regeneration step. It covers the **COM API7/API5** reference this project uses (interfaces named `iapplication_*`, `idocuments_*`, …). The cross-platform Qt/C++ **KsAPI** flavour (`ksapi_*`, non-COM binding) is intentionally **excluded** from the base — ignore unless explicitly working with KsAPI.
|
|
|
|
## Current state
|
|
|
|
**v1 + v2 + STEP/assembly + direct B-rep edit + structural model inspection implemented and working** (full sketch→feature→inspect→STEP round-trip + move_face + describe_model validated end-to-end). Stack:
|
|
**.NET 8 (`net8.0-windows`, x64), C#**, MCP via the official `ModelContextProtocol` SDK over **stdio**.
|
|
**55 MCP tools, 86 tests green (53 unit + 33 integration).** v2 added: sketch-on-face (by point and by index), revolve (boss/cut + sketch axis), fillet/chamfer (edge-by-point and by index), and Query tools `get_part_info` (МЦХ), `get_bounding_box`, `list_faces`, `list_edges`. After v2: `import_step`, `export_step` (`ConversionService`, `ConversionTools`), `list_components` (assembly traversal via `TopPart → IParts7`). `move_face` (`FaceEditService`, `EditTools`) — direct B-rep face editing. After move_face: **structural model inspection** — `describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure` (`ModelInspectionService`, `InspectionTools`). `describe_model` is the preferred first call over `model_snapshot` (no token cost for image context; gives feature tree, bodies, variables, topology summary, МЦХ). Package A «richer sketches»: 7 new sketch primitives — `sketch_add_arc`, `sketch_add_arc_3points`, `sketch_add_ellipse`, `sketch_add_polyline`, `sketch_add_polygon`, `sketch_add_spline`, `sketch_add_point`. `PartModeler` refactored into partial classes: `PartModeler.cs` (core), `PartModeler.Sketch.cs` (2D primitives), `PartModeler.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` class in `Core/Modeling`. Package B «forming ops»: **`shell`** (оболочка, `o3d_shellOperation=43`) + **`rib`** (ребро жёсткости, `o3d_ribOperation=44`). Additional Edit tools: **`split_solid_by_plane`**, **`move_body`**, **`boolean_union`**.
|
|
See [`README.md`](README.md) and [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md); design decisions/caveats in
|
|
[`docs/OPEN_QUESTIONS.md`](docs/OPEN_QUESTIONS.md).
|
|
|
|
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.
|
|
|
|
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` (vendored КОМПАС interop
|
|
assemblies from SDK `Samples/Common`, referenced via `Directory.Build.props` → `KompasInteropDir`).
|
|
|
|
```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)
|
|
```
|
|
|
|
Key implementation facts (don't relearn):
|
|
- **All COM calls run on one dedicated STA thread** (`KompasDispatcher.InvokeAsync`); КОМПАС is single-instance STA. Services never touch COM off that thread.
|
|
- Connection goes through **API5** (`KOMPAS.Application.5` → `KompasObject`) then `ksGetApplication7()` → `IApplication`; the API5 root is needed for `ksPart` 3D-building and `ksDocument3D` snapshots.
|
|
- 3D is built via **API5 `ksPart`** (`NewEntity(o3d_sketch/o3d_bossExtrusion/o3d_bossRotated/o3d_fillet/o3d_chamfer/o3d_shellOperation=43/o3d_ribOperation=44/...)`, `ksBossExtrusionDefinition`/`ksCutExtrusionDefinition`/`ksBossRotatedDefinition`/`ksFilletDefinition`/`ksChamferDefinition`/`ksShellDefinition`/`ksRibDefinition`); cut-through holes use `dtBoth` + `etThroughAll`. API7 is used for app/documents.
|
|
- **Face/edge selection**: `ksPart.EntityCollection(o3d_face|o3d_edge)` → `SelectByPoint(x,y,z)` (world mm) filters to objects through the point, then `GetByIndex(0)`; or by stable index from `list_faces`/`list_edges`. `ksFaceDefinition.IsPlanar/IsCylinder/...` + `GetArea(ST_MIX_MM)` classify faces. `ksEdgeDefinition.IsLineSeg/IsCircle/IsArc/...` + `GetLength()` classify/measure edges. Sketch axis = line with system style **3** (осевая). Fillet/chamfer by edge index via `fillet_edge_index`/`chamfer_edge_index` (more reliable than by-point).
|
|
- **МЦХ / 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**.
|
|
- Logs go to **stderr** (stdout is the MCP channel). Integration tests reuse one КОМПАС via `KompasFixture`; artifacts land in gitignored `.scratch/`.
|
|
- **Structural model inspection** (`ModelInspectionService`, `InspectionTools`): `describe_model` — one-call structural "passport" (bounding box + МЦХ + bodies + topology summary + feature tree + variables); preferred over snapshot. `list_features` — feature tree with params (depth/radius/legs). `list_bodies` — list solid/surface bodies with face count. `list_variables` — model variables (name/expression/value, external/info flags). `describe_face` — drill-down by index (type, area, normal, radius, edge count). `describe_edge` — drill-down by index (type, length, adjacent faces, vertices). `measure` — distance/angle between two objects (face|edge|vertex by index, unit `ST_MIX_MM`).
|
|
- **Feature tree API** (don't relearn): 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()` and matching definition type (`ksBossExtrusionDefinition.GetSideParam`, `ksFilletDefinition.radius`, `ksChamferDefinition.GetChamferParam`, …). Node name ("Элемент выдавливания:1", "Скругление:1") is localized by КОМПАС itself.
|
|
- **STEP import/export, `list_components`, `move_face`, `split_solid_by_plane`, `move_body`, `boolean_union`, full inspection layer, richer sketch primitives (package A), `shell` and `rib` (package B) are done.** Not yet done: remaining package B forming ops (draft/loft/sweep/hole); patterns/arrays (package C), parametrics (package D), 2D drawing, assembly building. Known caveat: boss/cut direction on a selected face depends on face-normal orientation (`forward` may need flipping — agent picks via snapshot or `describe_face` normal). See `docs/OPEN_QUESTIONS.md` → «Ревью v2».
|
|
- **"STEP import without history"** detected structurally: `bodyCount > 0 && no formative features && features.Count <= bodyCount+1` (only origin + body). STEP with edits (move_face: "Смещённая плоскость", "Разрезать", "Переместить грани", "Булева операция") — that already has history.
|
|
- **Topology/measure API**: `ksFaceDefinition.EdgeCollection` / `GetCylinderParam` / `GetSurface().GetNormal` + `normalOrientation`; `ksEdgeDefinition.GetAdjacentFace(bool)` / `GetVertex(bool)` → `ksVertexDefinition.GetPoint`; bodies via `ksPart.BodyCollection()` → `ksBody` (`IsSolid` / `FaceCollection`); variables via `ksPart.VariableCollection()` → `ksVariable`; measurements via `ksPart.GetMeasurer()` → `ksMeasurer` (`SetObject1/2`, `unit=ST_MIX_MM`, `Calc`, `distance`/`MinDistance`/`angle` in degrees / `IsAngleValid`).
|
|
- **STEP import COM pattern** (verified): `IApplication.get_Converter((object)(int)ksConverterFromSTEP=-3)` (pass format code, not DLL path) → `IConverter.ConverterParameters(cmd)` → set `IAdditionConvertParameters.Format=ksConverterFromSTEP` → `IKompasDocument3D1.ConvertFromAdditionFormat(path, prm)`. Export: `ConvertToAdditionFormat` with format codes for AP203/AP214/AP242. Param co-classes (AdditionConvertParameters etc.) are NOT CoCreatable — only via converter factories. Assembly traversal: `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 on top_spacer, 39.45→41.45 mm height): API7 face object retrieved via `IPart7.FindObjectsByPoint(x,y,z,true)` → cast to `KompasAPI7.IFace`; containers acquired at runtime via COM-QI: `(ISurfaceContainer)part`, `(IModelContainer)part`; `FaceMover` (SetFaces + Offset + Direction + Update) moves the face. distance>0 = outward (add material), <0 = inward. Works on both parametric and imported B-rep geometry. Full split-reposition workflow (SplitSolids/BodyRepositions) is driveable but not yet exposed as tools.
|
|
- **Shell operation (`shell`, package B)**: `ksShellDefinition` via `ksPart.NewEntity(o3d_shellOperation=43)`; `thickness` (mm) + `thinType` (bool: `true`=inward/`false`=outward; mapping: `thinType = !outward`) + `FaceArray()` (open faces, ≥1, by index from `list_faces`). Helper `SelectFaceByIndex` in `PartModeler.cs` (analogous to `SelectEdgeByIndex`, with range check). Validation: `thickness>0`, dedup via `Distinct`, range check before mutation. Transient RCWs in `ShellAsync`/`SelectFaceByIndex` are intentionally not released (consistent with Fillet/Chamfer pattern).
|
|
- **Rib operation (`rib`, package B)**: `ksRibDefinition` via `ksPart.NewEntity(o3d_ribOperation=44)`. Params: `sketchId`, `thickness` (mm), `side` (`left|right|up|down` → 0/1/2/3 via `SketchGeometry.RibSide()`), `symmetric` (bool), `angle` (°). `SetSketch(sketch)` → set `index=0`, `angle`, `side`; `SetThinParam(dtBoth, t/2, t/2)` when `symmetric=true`, else `SetThinParam(dtNormal, t, 0)`. Empirical: the rib contour must float in the gap (not touch the body at endpoints) — КОМПАС extends the web to the body automatically.
|
|
- **Richer sketch primitives (package A, ksDocument2D API5 wrappers)**: `ksArcBy3Points` (3-point arc), `ksArcByAngle` (centre/radius/start-end angles in degrees, counterClockwise flag), `ksEllipse` via `ksEllipseParam` — struct obtained via `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 are released after use. `PartModeler` split into partial classes: `PartModeler.cs` (registry/helpers/NewParam<T>/reset), `PartModeler.Sketch.cs` (all 2D primitives), `PartModeler.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` class (`Core/Modeling`) holds enum mappings (`ArcDirection`, `PolygonDescribe`) and validators (`RequirePositive`, `RequireVertexCount`, `RequirePoints`). Polyline/spline point lists use `record SketchPoint(X, Y)` with 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**:
|
|
|
|
| | 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 |
|
|
|
|
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` — 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`, …).
|
|
|
|
> 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.
|
|
|
|
## 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`.
|
|
- `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.
|
|
|
|
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).
|
|
|
|
## 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.
|