mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 19:06:56 -06:00
## 변경 내용 - entities 명령에 selected 불리언 필터를 추가해 현재 공간의 선택 객체만 조회 - 기존 종류·레이어·영역 필터와 결합하고 필터 적용 후 페이지 처리 - MCP query_entities 스키마와 명령·제어 프로토콜 문서에 옵션 공개 - 빈 선택, 중복·오래된 핸들, 다른 공간 객체, 잘못된 입력 및 조회 전후 상태 보존 회귀 테스트 추가 ## 검증 - cargo xtask ci 6단계 통과: fmt, clippy, 테스트 2,012개, assets, layers, wasm - 별도 .NET 클라이언트와 실제 헤드리스 엔진의 localhost 통합 검증 통과 - 실제 데스크톱 UI 조작 및 고객 DWG 왕복 검증은 수행하지 않음 Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
4.8 KiB
Markdown
69 lines
4.8 KiB
Markdown
# CADCraft and AI agents (MCP)
|
|
|
|
CADCraft speaks the [Model Context Protocol](https://modelcontextprotocol.io) over stdio.
|
|
|
|
## Start it
|
|
|
|
```sh
|
|
# Headless: an in-process drawing session, no window.
|
|
cadcraft-cli mcp
|
|
|
|
# Bridged to the running app (watch the agent draw):
|
|
cadcraft --control 7979 &
|
|
cadcraft-cli mcp --connect 127.0.0.1:7979
|
|
```
|
|
|
|
An MCP client usually starts its servers when it starts, often before the app is open. With
|
|
`--lazy` the bridge starts anyway and reaches the app at the first tool call; until the app answers,
|
|
each call fails with a message saying how to open it. `--launch` also opens CADCraft (macOS) with
|
|
that control port at a call that finds nothing, and waits for it:
|
|
|
|
```json
|
|
{ "mcpServers": { "cadcraft": { "command": "cadcraft-cli", "args": ["mcp", "--connect", "127.0.0.1:7979", "--launch"] } } }
|
|
```
|
|
|
|
`--connect` needs the app's `HOST:PORT`. Without one (or with an unknown argument) `cadcraft-cli mcp`
|
|
exits with an error instead of falling back to a headless session, so a client meant for the app
|
|
never edits a separate drawing by mistake. If the app can't be reached, startup fails too.
|
|
|
|
Example client configuration (Claude Code / Claude Desktop):
|
|
|
|
```json
|
|
{ "mcpServers": { "cadcraft": { "command": "cadcraft-cli", "args": ["mcp"] } } }
|
|
```
|
|
|
|
## Tools
|
|
|
|
| Tool | What it does |
|
|
|---|---|
|
|
| `command_line {text}` | Type at the command line exactly like a person: `line 0,0 @10,0 @0,5 c`, `circle 5,2 1`, `offset 0.5`, `zoom e`. Spaces act as Enter; empty text is Enter. Returns the new prompt, keywords and output. |
|
|
| `script {text}` | A multi-line script (like `.scr` files). |
|
|
| `execute {command, params}` | Any command with JSON parameters, never a dialog. `list_commands` shows ids and parameter docs. |
|
|
| `list_commands {filter?}` | The command catalog; `filter` matches part of a command's id, label or an alias. |
|
|
| `inspect_drawing {entities?, limit?}` | Counts, extents, layers, styles, blocks, layouts, selection, undo history and entities. |
|
|
| `query_entities {type?, layer?, window?, crossing?, limit?, offset?}` | Filtered entities with handles and geometry. `window` `[[x0,y0],[x1,y1]]` finds objects whose geometry is inside it or crosses it (`crossing: false`: entirely inside). `count` is every match, `returned` the page (`offset`, `limit`, default 500). |
|
|
| `execute {"command": "shapes", "params": {type?, layer?, window?, crossing?, limit?, offset?, tolerance?}}` | Facts about the closed shapes — closed polylines, circles, full ellipses, closed splines: perimeter, area, centroid, winding, the first self-intersection and coincident vertices, exact with arcs and bulges. Other matches are counted under `skipped` with the reason. |
|
|
| `query_entities {type?, layer?, selected?, window?, crossing?, limit?, offset?}` | Filtered entities with handles and geometry. `selected: true` restricts results to the current selection in the active space; an empty selection returns no entities. Omitted/false queries all entities in that space. Filters combine before paging. `window` `[[x0,y0],[x1,y1]]` finds objects whose geometry is inside it or crosses it (`crossing: false`: entirely inside). `count` is every match, `returned` the page (`offset`, `limit`, default 500). |
|
|
| `render {width?, height?, fit?, window?}` | A PNG image of the drawing, or of just the area `window` `[[x0,y0],[x1,y1]]` (one sheet or room of a large drawing; 1600 px wide with the window's shape unless sized). |
|
|
| `find_text {contains?, layer?, window?, limit?, offset?}` | The readable texts — TEXT, MTEXT without formatting codes, block attributes, multileaders — with text, insertion point, height, rotation, style, layer and handle; filtered by content (any case), layer and a window around the insertion points. Read-only (the `texts` command). |
|
|
| `new_drawing {metric?}`, `open {path}`, `save {path?}`, `cancel` | Files and Escape. `save` to a `.svg`, `.png` or `.pdf` path exports: the drawing keeps its name and unsaved changes. |
|
|
| `screenshot`, `ui_inspect`, `ui_click {x, y}` | Only when connected to the app. |
|
|
|
|
Resources: `cadcraft://drawing` (inspect JSON) and `cadcraft://commands` (catalog).
|
|
|
|
Arguments are checked against each tool's input schema. An unknown tool, `arguments` that aren't an
|
|
object, a missing required argument or one of the wrong type is a JSON-RPC error `-32602` (invalid
|
|
params), so `command_line {}` never presses Enter by accident. A tool that runs and fails (a bad
|
|
point, an unknown command) returns a result with `isError: true` and the message.
|
|
|
|
## A typical session
|
|
|
|
1. `command_line {"text": "rectang 0,0 100,60"}`
|
|
2. `execute {"command": "circle", "params": {"center": [10, 10], "radius": 4}}`
|
|
3. `command_line {"text": "dimlinear 0,0 100,0 50,-8"}`
|
|
4. `inspect_drawing {}` — verify counts and geometry.
|
|
5. `render {}` — look at it.
|
|
|
|
The end-to-end test in `crates/mcp/src/tests.rs` runs a session like this and checks the result
|
|
only through MCP.
|