Files
mikhail 5d51b9e1af docs: выноска — TODO, README, ARCHITECTURE, OPEN_QUESTIONS, presentation, CLAUDE.md
Инкремент 9. 83 инструмента, 331 тест. + заметка о ревью реализации в спеке
(Codex + pi/glm-5.1 + pi/kimi-k2.6).

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

139 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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, dispatch the **`kompas-sdk-research`** subagent (Haiku) 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
**Status:** v1 + v2 + STEP/assembly + direct B-rep edit + structural inspection + 2D drawings — 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**. **83 MCP tools, 331 tests green (201 unit + 130 integration).**
Where to look (single source of truth — do **not** duplicate these lists here):
- **Full tool catalog** (by group, all 83) → [`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`).
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.
**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 — `Glob`/`Grep`/`Read`): finds an interface/method/enum/constant signature in the `docs/Kompas3D_SDK/` MD knowledge base and returns a compressed summary. Dispatch it for any non-trivial SDK lookup instead of grepping the help yourself; 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` (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**: 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`).
- **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).
- 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`).
## КОМПАС-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.