AI assistants (MCP)
A design program can expose itself to an AI assistant over the
Model Context Protocol — headlessly, with no
window. The assistant can then list the model’s parts, measure them, read how they
were built, export them, and see them: the screenshot tool renders the scene and
returns the PNG as an image, so questions about shape and proportion get answered by
looking rather than guessing.
Swap the entry point from EngrCad.Run to EngrCadMcp.Run (package
EngrCAD.Mcp):
using EngrCAD.Mcp;using EngrCAD.Modeling;
return EngrCadMcp.Run(args, BuildScene, "bracket");
static Scene BuildScene(){ var scene = new Scene(); scene.Add(new Part("bracket", Shape.Extrude(Sketch.RoundedRectangle(40, 24, 5), 8) .Drill(StandardHoles.Clearance(5), [new(-14, 0), new(14, 0)], depth: 10, SketchPlane.At((0, 0, 8), Vector3d.UnitX, Vector3d.UnitY)))); return scene;}That adds one switch, --mcp. Everything else is untouched: no arguments still opens
the live dotnet watch loop, and --view, --export, --render, --section,
--render-style behave exactly as before.
dotnet run --project MyModel -- --mcp # serve this model over MCP on stdiodotnet run --project MyModel # unchanged: the live viewer loopConfiguring a client
MCP clients launch the server as a child process and talk to it over stdin/stdout:
{ "mcpServers": { "bracket": { "command": "dotnet", "args": ["run", "--project", "samples/MyModel", "--", "--mcp"] } }}For a faster start-up (no build check per launch), point it at a published binary instead:
{ "mcpServers": { "bracket": { "command": "C:/models/bracket/bracket.exe", "args": ["--mcp"] } }}What the assistant gets
| Tool | Answers |
|---|---|
list_tabs | What tabs exist, and how much is in each. |
list_parts | Every part: name, tab, geometry kind (Shape / B-Rep / mesh / SDF), how many times it is placed and where, colour, display mode, whether it has an exact B-Rep route. |
describe_part | Faces, vertices, closed, volume, surface area, local and world bounding boxes, placement, annotations — and the construction tree, the ordered record of how the part was built (booleans, drills, fillets, sketches; or the parametric feature list with its [Param] values). |
screenshot | A rendered PNG. Standard views (iso, front, back, left, right, top, bottom), display styles (shaded-edges, shaded, wireframe, points), a section plane (sectionAxis + sectionOffset) that cuts the model open to show bores and wall thickness, image size, an optional tab/part filter, and t — a position on the program’s animation timeline, so an assistant can ask for the mechanism at half stroke. |
export | Writes .step (exact B-Rep, one file per part), .stl/.obj/.off (meshes merged with instance transforms), .3mf/.amf (one named object per part), .glb/.gltf (the assembly hierarchy kept), .vtu (geometry plus every part’s simulation results, the array count reported), or .png. |
set_param, suppress_feature, unsuppress_feature | Edit a [Param] value or toggle a feature’s suppression on a history-backed part and regenerate. The result is the regeneration report; a failure keeps the previous geometry and names the feature. |
save_document, load_document | Write the whole model — tabs, parts with their histories, assemblies, mates, annotations, results — as one document file, and read one back. |
reload | Re-runs the scene factory after the model’s source changed — the headless equivalent of hot reload. A model that throws leaves the previous scene in place and reports the error. |
There is also a resource, engrcad://scene: the whole document as JSON, cheap enough
to read on every turn.
Editing, and getting the edits back out
set_param changes the running model; the program’s source stays the truth, so
reload discards session edits by design. That is the right default and it leaves one
obvious gap — how does the tuning an assistant just found reach the user? Through
save_document:
set_param(part: "bracket", feature: "Base", param: "Height", value: 14)save_document(path: "bracket-tuned.json")The file is the same envelope documents describes, so it reopens
parametric: load_document regenerates the histories and the write tools keep
working on the result. The corollary is reported rather than hidden — a part with no
construction recipe (a raw mesh, an imported STL, an Sdf) travels as a mesh snapshot,
and both tools list those parts by name. A loaded document is a session-lifetime
overlay: reload still re-runs the program’s own source and discards it, so there is
one answer to where the model comes from rather than two.
Two things worth knowing
stdout is the protocol. The stdio transport is standard output, so a single
Console.WriteLine in model code would corrupt the stream and break the client.
--mcp mode redirects Console.Out to stderr before the scene factory ever runs and
keeps the raw stdout handle for protocol frames, so printing from a model is safe —
it shows up in the client’s server log instead.
Nothing is meshed up front. Tessellating a busy scene costs tens of seconds, and
most questions need no geometry: list_tabs, list_parts and reload evaluate none
at all, describe_part meshes only the part it was asked about, and screenshot and
export mesh only what they are about to draw or write.
Hosting it yourself
The pieces are separable, so the tools can be embedded in a larger server or driven over another transport:
var session = new SceneSession(BuildScene); // the live scene, plus reloadvar tools = new SceneTools(session); // one method per toolvar options = EngrCadMcpServer.BuildOptions(tools, "bracket");
await EngrCadMcpServer.RunAsync(input, output, tools, "bracket", cancellationToken);