MCP server for parametric CAD model generation, introspection, and rendering using CadQuery (OpenCASCADE)
This is a well-engineered MCP server with clear, detailed tool descriptions and comprehensive input/output schemas. All 5 tools have non-empty descriptions (avg ~250 chars, well within the 10-1024 target). Parameters are consistently typed and described. The main weakness is the absence of tool annotations (readOnlyHint/destructiveHint) and minimal guidance on error recovery. The codebase shows strong discipline: handlers are SDK-free, schemas are explicit, and parameter validation is thoughtful (e.g. 'match' enum on query_engine_class, validation of class_key ambiguity). However, output schema documentation is implied rather than formally declared in the tool definitions, and error messages lack recovery guidance per pattern:recovery-guide.
Compile a parametric solid from a class name and parameters (R3). Instantiates ``class_key`` with the supplied ``parameters`` dict, reads its ``.solid`` property (a CadQuery ``Workplane``), and returns the result alongside metadata (class fqn, parameter echoes, error details if the compile failed). The solid is **not** persisted or exported here — the result feeds the preview / export pipeline downstream (preview → SVG, export_solid → STEP/STL).
Export a compiled solid to STEP or STL binary format (R5). Takes the ``solid`` dict returned by ``compile_solid`` and writes it to a binary STEP (ISO 10303-21) or STL (binary or ASCII) file in a temp directory. Returns the file path; the client may then read/download it. Never inlines binary data (bloats the JSON-RPC frame). Deterministic: same solid ⇒ same binary (STEP) or equivalent geometry (STL, which has rounding due to mesh).
Return the class index from the committed ``engine_api.json`` (R2). Optional filters ``module_prefix`` (startswith on fqn/module) and ``name_contains`` (case-insensitive substring on the short name). Both absent ⇒ the full index. Deterministic: same JSON ⇒ same output.
Render a compiled solid to SVG and return the image (R4/R9). Takes the ``solid`` dict returned by ``compile_solid`` and renders it to an orthographic 2D SVG snapshot. The SVG is returned **inline** (base64 or text) if it fits under ``MAX_INLINE_SVG_BYTES`` (256 KiB); larger SVGs are written to a temp file and returned as a file path. Deterministic given the same solid: same layout, same styling, same SVG bytes — no random seeds or timestamps.
Tool annotations (readOnlyHint, destructiveHint, idempotentHint) are absent. The export_solid tool performs a WRITE operation (creates temp files) but has no destructiveHint annotation. This prevents clients from rendering appropriate warnings or capability filtering.
Error handling lacks recovery guidance. When compile_solid fails (e.g. invalid parameters, instantiation error) or query_engine_class returns 'class_not_found' or 'ambiguous_class', the error response does not suggest next steps (e.g. 'Call list_engine_classes() to find valid class names').
Output schemas are documented in prose within tool descriptions but not formally declared in the tool registration. For example, preview_solid returns either inlined SVG/PNG or a temp file path, and export_solid returns a file path, but these return types are not structured in a way LLMs can parse programmatically.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 63 | 2026-07-28+ | v2 |
Return the full class record for ``class_key`` (R2). Lookup is **exact, not fuzzy**. ``class_key`` matches ``fqn`` first. If ``match='short'`` OR the key contains no ``.``, it falls back to matching the short ``name``; an *ambiguous* short name (>1 class shares it) returns an ``ambiguous_class`` error listing the candidate fqns rather than silently picking one. A miss returns ``class_not_found``.
Parameters like 'parameters' in compile_solid accept an arbitrary dict without schema validation constraints (no maxProperties, no schema for nested values). This invites passing invalid keyword arguments; error messaging would improve if the tool validates against the target class's __init__ signature and returns 'Invalid parameter X for class Y. Valid params: [list]'.
No pagination or result limits declared. list_engine_classes applies filters but may return large class indexes. If the committed engine_api.json contains hundreds of classes, the response could bloat the context window. The tool description should state a result cap (e.g. 'Defaults to 50 classes; see pagination params').