Files
cadcraft/docs/mcp.md
T
78c1653f50 loops: implied closed loops from loose LINE and ARC chains (#1906)
* loops: implied closed loops from loose LINE and ARC chains

LOOPS is a read-only inquiry command that chains LINE and ARC endpoints
meeting within a tolerance. Closed loops report their member handles,
perimeter, area and winding (exact with arcs, like SHAPES); open chains
report the endpoint gap, where it is and their length; branch junctions
(three or more ends at one point) are reported but excluded from the
chains rather than resolved.

The tolerance defaults to 1e-9 x the extents diagonal and is an option.
The quadratic endpoint pairing is capped (maxPairs, 1,000,000 by default)
and reports capped/pairTestsNeeded instead of guessing from a partial
pairing. Degenerate lines and arcs are counted under skipped; full-circle
arcs close on their own. The command is read-only.

Fixes #1437

* loops: cap maxPairs at 100,000,000 so a caller can't ask for unbounded work

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:51:11 -04:00

5.4 KiB
Raw Blame History

CADCraft and AI agents (MCP)

CADCraft speaks the Model Context Protocol over stdio.

Start it

# 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:

{ "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):

{ "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.
execute {"command": "loops", "params": {tolerance?, maxPairs?, limit?, offset?}} Implied closed loops from loose LINE and ARC chains: each closed loop with its member handles, perimeter, area and winding, exact with arcs; each open chain with the endpoint gap, its location (at, ends) and length; and branch junctions (three or more ends meeting), reported but not resolved. tolerance defaults to 1e-9 × the extents diagonal; the quadratic endpoint pairing stops at maxPairs (default 1,000,000) and reports capped with pairTestsNeeded instead of guessing from a partial pairing.
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.