Skip to main content
Glama

cad-mcp

ci python runtime deps license

An MCP server that lets an LLM design parametric 3D parts — and actually verify they are printable.

Most "AI + CAD" demos stop at generating geometry. That is the easy half. The hard half is that a mesh can look correct in a screenshot and still be a broken solid — and the model has no way to notice.

cad-mcp closes that loop. Every part the model builds is checked for topology before it can be exported, and the verdict comes back as structured numbers (watertight? genus? volume? wall thickness?) instead of a picture the model has to squint at.

you:    make me a 19-tooth helical gear with a 12 mm bore

model:  make_part("involute_helical_gear", {"teeth": 19, "bore_dia": 12})
        → watertight=True  genus=1  volume=31140.45 mm³  19,912 triangles
        └ genus=1 is the check that matters: exactly one through-hole, i.e. the bore.
          genus=0 would mean the bore is missing; genus=2 would mean the blank is split.

model:  look_at_mesh("gear", views="four") → the image below comes back into the context
        export_mesh("gear", ["stl", "obj"]) → out/gear.stl

19-tooth helical gear

Rendered by the server itself, in pure NumPy — no OpenGL, no CAD kernel. 19 teeth, module 2.5, 22° helix, 16 mm face width, DIN 6885 keyway. Watertight, genus 1, 31,140.45 mm³.

Four parts

What a refusal looks like

The interesting case is not a part that builds. It is a part that builds, looks fine, and is not printable. Ask for a lattice with walls thinner than a voxel and this comes back:

{
  "ok": false,            "watertight": true,
  "genus": -928.0,        "shells": 737,
  "fragmented": true,     "volume_mm3": 11.7538,
  "warning": "The mesh shattered: 737 disconnected shells, overall genus -928 (negative). This is not a part, it is a pile of flakes; exporting will be refused. Raise the wall thickness or lower res."
}

Every signal a human would check says fine: watertight, zero non-manifold edges, positive volume, and a screenshot that looks like a dense lattice. The mesh is 737 disconnected shards, each one a perfectly closed surface — which is why only the shell count and the sign of the whole-mesh genus reveal it. export_mesh then refuses before touching the disk; force=True overrides, and says so in its return value.

A full session — build, verify, look, change the bore, export, get refused — is recorded in docs/demo-session.md, generated by scripts/make_session_demo.py against the real server. Every JSON block in it is an actual tool return value, including an independent check that predicts a volume the server later reports to 0.01%.

Related MCP server: fcgen-mcp

Install

python -m venv .venv
.venv/bin/pip install -e ".[render]"      # Windows: .venv\Scripts\pip

cad-mcp needs Python 3.10+ and NumPy. The render extra adds Pillow, which is only required for the visual half (look_at_mesh). Without it, the modelling and verification tools still work. There is no CAD kernel, no OpenCASCADE, no trimesh, no compiled extension.

Modelling every built-in part — including the vase, whose radius profile is a smooth spline — works on that install alone. The spline has two implementations: scipy.interpolate.CubicSpline when scipy is present, and a built-in NumPy one otherwise. They agree to 4.4e-16, and tests/test_kernel.py asserts that, because "scipy is optional" is only true if the parts actually build without it. Verified by installing NumPy alone into a fresh venv: all four parts build, and the vase comes out with bit-identical vertices (12432×3, max|Δ| = 0.0) and the same 62,960.4021 mm³ as the scipy environment.

Check the install before wiring it into a client:

.venv/bin/python -m cadmcp.server --check     # versions, renderer, font, output dir

Troubleshooting

Almost always a non-ASCII path on Windows. An editable install works by dropping a .pth file into site-packages; pip writes it as UTF-8, but site.py decodes it with the locale encoding (cp936 on a Chinese Windows). The path decodes to mojibake, the directory does not exist, and Python silently skips it — pip list still shows cad-mcp 0.1.0, so the install looks fine while the import keeps failing.

Three ways out, best first:

  1. Give the server the source tree directly — no .pth involved. Add PYTHONPATH to the client's env (see the last config block below).

  2. Or do a regular install instead of an editable one: pip install ".[render]".

  3. Or move the checkout somewhere ASCII-only, e.g. C:\dev\cad-mcp.

Pillow is missing. pip install "cad-mcp[render]". Modelling and verification work without it.

The bundled fallback font has no CJK glyphs. --check prints which font was picked. Point CADMCP_FONT at any font file that has the glyphs, e.g. C:\Windows\Fonts\msyh.ttc, and pass it in the client's env.

Configure your client

{
  "mcpServers": {
    "cad": {
      "command": "/absolute/path/to/cad-mcp/.venv/bin/python",
      "args": ["-m", "cadmcp.server"],
      "env": { "CAD_MCP_OUT": "/absolute/path/to/output/dir" }
    }
  }
}

On Windows the interpreter lives at .venv\Scripts\python.exe, and backslashes in JSON have to be doubled:

{
  "mcpServers": {
    "cad": {
      "command": "C:\\dev\\cad-mcp\\.venv\\Scripts\\python.exe",
      "args": ["-m", "cadmcp.server"],
      "env": { "CAD_MCP_OUT": "C:\\dev\\cad-mcp\\out" }
    }
  }
}
claude mcp add cad -- /absolute/path/to/cad-mcp/.venv/bin/python -m cadmcp.server

Run the server as a subprocess and speak MCP over stdin/stdout:

command: /absolute/path/to/cad-mcp/.venv/bin/python
args:    ["-m", "cadmcp.server"]
env:     CAD_MCP_OUT=/absolute/path/to/output/dir   # optional, defaults to ./out

CAD_MCP_OUT is the only knob. Everything the server writes — STL, OBJ, PNG, GIF — lands there.

That is the whole setup. If you would rather run the server straight out of a checkout without pip install -e ".[render]", point Python at the source tree instead:

{
  "mcpServers": {
    "cad": {
      "command": "/absolute/path/to/cad-mcp/.venv/bin/python",
      "args": ["-m", "cadmcp.server"],
      "env": {
        "PYTHONPATH": "/absolute/path/to/cad-mcp/src",
        "CAD_MCP_OUT": "/absolute/path/to/output/dir"
      }
    }
  }
}

The seven tools

Tool

What it does

list_parts

Lists the built-in parametric parts and their parameters, with defaults and ranges read from the implementation, so the model never has to guess an argument name

make_part

Instantiates a parametric part (involute_helical_gear, twisted_vase, torus_knot_tube, lattice_section_sample) and returns its topology verdict

make_lattice

Builds free-form geometry from signed distance fields: spheres, boxes, gyroid / Schwarz-P TPMS lattices, shells, with optional corner notches to expose the interior. Reports ok=false (and writes nothing) if what came out is fragmented

check_mesh

Re-runs the topology report, cuts cross-sections, estimates minimum wall thickness — pass/fail gates for printability. Reports printable separately from ok: the check running and the part being printable are two different questions. Also reports shell count and a fragmented flag: a watertight mesh whose overall genus is negative is not a part, it is a pile of shards

look_at_mesh

Renders one or more viewpoints (or a turntable GIF) and returns the images as MCP image content, so the model sees its own work

export_mesh

Writes binary/ASCII STL or OBJ+MTL. Refuses at the gate: non-manifold edges, non-positive volume, NaN vertices, or a fragmented shell are blocked before anything touches the disk. Non-watertight only warns. Override with force=True

describe_model

Replays a session model: the exact parameters it was built from, its topology, and every artifact on disk. This is what makes "now make the bore 14 mm" work without the model having to remember its own earlier tool call

Arguments like involute_helical_gear(teeth=19, module=2.5, helix_deg=22) are validated against the real signature — call it with teath=19 and you get involute_helical_gear 不认识这些参数: ['teath'] instead of a silently wrong gear.

One thing worth knowing when writing a client: a wrong type is caught earlier, by the SDK's own argument validation, and comes back as isError=true with a plain-text message — there is no JSON body for that case. Only the field name reaches the server log. So parse the JSON payload of a tool result, but check isError first.

Why the verification numbers are trustworthy

cad-mcp does not grade its own homework. The checks below compare the tessellated mesh against closed-form results computed independently of the mesher, so a bug in the mesher cannot cancel itself out. All values are reproducible with python scripts/make_verification.py and stored in docs/verification.json.

Check

Measured

Independent value

Error

Gear circular tooth thickness at the pitch circle

3.92488 mm

πm/2 − backlash/2 = 3.90699 mm

+0.458 %

SDF sphere volume, r = 10 mm, 96³ grid

4184.7218 mm³

4/3·πr³ = 4188.7902 mm³

0.097 %

Binary STL round-trip (gear, 19,912 tris)

995,684 bytes, volume Δ 0.0 mm³

84 + 50n = 995,684 bytes

exact

Gear minimum wall thickness (240 samples)

2.1124 mm (p05 = 2.4258, median = 7.5016)

—

—

The residual 0.458 % on tooth thickness is not a bug: it is the polygon approximation of the involute flank at the pitch circle (12 flank samples per tooth). Tightening flank_samples converges it toward πm/2, which is exactly the behaviour you want a validator to show.

Every part below is watertight with zero non-manifold edges:

Part

Triangles

Genus

Volume

Notes

involute_helical_gear (19T, m2.5, 22°)

19,912

1

31,140.45 mm³

genus 1 = the bore, nothing else

twisted_vase

24,860

0

62,960.36 mm³

hollow, superellipse cross-section

torus_knot_tube

18,480

1

125,254.19 mm³

swept tube, self-intersection free

lattice_section_sample (gyroid)

128,284

87

—

33.3 % solid fraction

SDF gyroid, 64³ grid

33,900

197

2,682.08 mm³

20 mm cube, 0.625 mm voxels

Speed

Single-threaded, pure NumPy, on an ordinary desktop CPU (scripts/make_verification.py):

Operation

Time

make_part gear, 19 teeth (0.5 s of that is involute + fillet tessellation)

0.54 s

make_part vase

0.09 s

make_lattice gyroid, 64³

0.07 s

check_mesh, 3 cross-sections

0.003 s

export_mesh binary STL

0.009 s

render one 960×720 view

3.5 s

render 4-view grid, 320 px each

2.3 s

render 12-frame turntable GIF

4.5 s

Modelling is cheap; rendering is the slow part, which is why look_at_mesh takes a views count and a size — ask for one 640×480 view while iterating, four larger ones when you are done.

What is inside

src/cadmcp/
├── geom.py      34 KB  Mesh, welding, topology report (Euler characteristic, genus,
│                       manifold audit), slicing, min wall thickness, ear-clipping,
│                       solid/tube sweeps
├── models.py    16 KB  the four parametric parts, involute flanks + root fillets
├── implicit.py  36 KB  SDF primitives, booleans, smooth min/max, twists, gyroid /
│                       Schwarz-P, surface-nets mesher
├── render.py    52 KB  software rasteriser: supersampled triangle fill, Phong shading,
│                       contact shadows, ground pool, perspective/ortho cameras,
│                       turntables, multi-view grids
├── meshio.py     5 KB  binary/ASCII STL, OBJ+MTL round-trip
└── server.py           the MCP layer

The kernel is deliberately analytic where it can be, numeric where it must be: parametric parts are exact sweeps of closed profiles, while lattices come out of an implicit field.

Four implementation notes that cost real debugging time, in case you fork this:

  • sdf_to_mesh(f, lo, hi, res) takes a resolution count per axis, not a voxel size. Passing a voxel size (~0.3) makes min(nx,ny,nz) < 2 and the mesher refuses. The compute domain is also padded 12 % so the iso-surface never touches the boundary.

  • Pillow's built-in font has no CJK glyphs. Every Chinese label in a render_grid came out as □□□ — in the exact images meant for this README. The renderer now resolves a system CJK font (Microsoft YaHei / SimHei / PingFang / Noto CJK), centres the label on its real glyph bbox, and exposes font_supports_cjk() so callers can fall back to English. Override with CADMCP_FONT.

  • A watertight mesh can still be garbage. Set a lattice wall thinner than one voxel and the surface-nets mesher happily returns 737 disconnected shards — every one of them a perfectly closed surface, watertight=True, non_manifold_edges=0, positive volume. Every "does it look broken?" signal says no. What catches it is the shell count, plus the fact that genus, being a whole-mesh formula (g = (2 − χ)/2), turns negative when the mesh is really a pile of fragments. That is why export_mesh refuses on fragmented and why check_mesh reports shells — the shard detection is the check that a picture could never have made.

  • Vertex normals belong to a smoothing sector, not to a vertex. The gear rendered with a fine vertical stipple across its hub and tooth tips while flat panels and every other part came out clean. Three hypotheses died on data first: six specular/shininess combinations, eight tessellation settings, and crease angles from 5° to 90° all produced the identical stipple — the last one because a fixed count of 92 edges sit at exactly 180°, independent of sampling. The cause was real: normals were one area-weighted average over every incident face, and the hub cylinder's vertices sit directly against a coplanar annular face, which dragged the average toward +Z. Measured on the tip cylinder, normals deviated from radial by a median of 26.4° and up to 81.2°. They are now accumulated per smoothing sector — connected components along non-hard edges, the equivalent of a smoothing group. Smooth surfaces collapse back to one sector, so the fix is provably inert where it should be, and tests/test_kernel.py asserts both directions: the torus knot matches the naive average to 1e-6°, the gear does not.

Development

python tests/test_e2e_stdio.py        # 62 checks, drives a real server subprocess over stdio
python tests/test_kernel.py           # geometry kernel unit tests
python scripts/preflight.py           # 54 checks that every claim in THIS README still holds
python scripts/make_demo.py           # regenerate docs/gallery.png + docs/gear-turntable.gif
python scripts/make_verification.py   # regenerate docs/verification.json
python scripts/make_session_demo.py   # regenerate docs/demo-session.md

scripts/preflight.py exists because a README is the part of a project that rots silently. Code gets refactored, an argument is renamed, a verification number is recomputed — and no unit test notices, because unit tests test code. So every factual claim in this file is an assertion: it checks that each referenced file exists, that the numbers printed in the verification table match docs/verification.json character for character, that CI really runs both test scripts on all three platforms, and that the four built-in parts still build with the genus this README claims.

The end-to-end test is not a mock: it spawns python -m cadmcp.server, speaks the real MCP protocol over stdio, calls every tool, and asserts that look_at_mesh returns an image content block before the text summary — that ordering is what makes the model look at the picture instead of skipping it.

Both test scripts reconfigure their output to UTF-8 on startup. A Chinese Windows console defaults to cp936, where a single mm³ in an assertion message raises UnicodeEncodeError — turning a clean pass/fail report into a stack trace. CI hides this behind PYTHONIOENCODING: utf-8; running the tests locally does not, so they fix it themselves.

Limitations

  • Three parts families, not a general CAD kernel. No fillets/chamfers on arbitrary edges, no B-rep booleans, no constraints or assemblies.

  • Models live in server memory for the session. Restart the server and they are gone — export what you want to keep.

  • look_at_mesh needs Pillow; the modelling and verification tools do not.

  • STL/OBJ only. No STEP, so nothing here round-trips back into a parametric CAD system.

  • SDF meshing is grid-bound: sharp features below one voxel are lost, and res is a memory knob as much as a quality knob (capped at 200 M voxels).

License

MIT — see LICENSE.

Available Tools

7 tools
check_meshA

Geometrically inspect a model you already built; returns numeric evidence you can use to decide whether it came out right.

Includes watertightness, orientation consistency, Euler characteristic, genus, volume, surface area and bounding box. genus tells you whether the hole count is right (a part with one through-hole has genus=1, a solid has genus=0, a lattice coupon is high). It is a topological invariant, which makes it far more reliable than "looks about right". slices: z heights in mm to also measure cross-section areas; wall_samples>0 samples the minimum wall thickness.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
slicesNo
wall_samplesNo

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. 'Inspect' strongly implies a read-only, non-mutating operation and the output list is transparent, but it never explicitly confirms safety, and it omits any cost or performance warning for the potentially expensive wall_samples sampling mode.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose and output list are front-loaded in the first two sentences, and the optional-parameter behavior is grouped at the end. The genus paragraph is useful but slightly verbose, with a mild sales pitch ('far more reliable than "looks about right"') that is not strictly necessary.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema or annotation coverage, so the description must convey return content, and it does so by enumerating every metric returned plus the semantics of genus. It is nearly complete for this inspection tool; only the 'name' parameter and any cost caveats remain unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate, and it does for two of three parameters: slices is explained as 'z heights in mm to also measure cross-section areas' and wall_samples as sampling minimum wall thickness when >0. The required 'name' parameter is left undefined, though its meaning is self-evident from context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific verb and resource ('Geometrically inspect a model you already built') and enumerates the concrete outputs (watertightness, genus, volume, etc.), so an agent knows exactly what comes back. It does not explicitly contrast itself with the similar-sounding siblings look_at_mesh or describe_model, leaving that differentiation to inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear context of use: 'returns numeric evidence you can use to decide whether it came out right,' and notes the optional slices/wall_samples modes for deeper checks. It does not state when NOT to use it or name an alternative sibling, so the routing guidance is incomplete.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

describe_modelB

Full information about a model you built: parameters, topology, bounding box, files.

Use it mid-session to recall which parameters this model was actually built from.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It usefully discloses the kinds of information returned, but does not state that the operation is read-only, whether it has side effects, what happens if the model does not exist, or any permission requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two short sentences with no wasted text, and it front-loads the returned information before the usage note. It could be slightly more structured, but it is efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description must explain return values, and it does list major categories. It remains incomplete for the single required parameter and for error or not-found behavior, leaving some gaps for an agent invoking the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is one required parameter, 'name', with 0% schema description coverage. The description implies the name identifies 'a model you built', but does not explain the expected format, uniqueness, or source of the name, so it fails to compensate for the missing parameter documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific retrieval action and enumerates what information is returned: parameters, topology, bounding box, and files. It is clearly a metadata lookup for a built model, though it does not explicitly name how it differs from siblings like list_parts or check_mesh.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a concrete usage occasion: mid-session, to recall the parameters a model was built from. However, it does not name alternatives or describe when not to use this tool, leaving routing guidance implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

export_meshA

Export the model as files for downstream tools: STL (printing / slicing), OBJ+MTL (DCC).

Defaults to both STL and OBJ. There is a hard gate before anything is written: structural damage -- non-manifold edges, non-positive volume, NaN vertices, a fragmented shell -- is refused, because such a mesh punches holes in a slicer or errors out. Watertightness only warns (some shapes are meant to be open). Pass force=True to override.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
forceNo
formatsNo
stl_binaryNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses a hard pre-write gate (non-manifold edges, non-positive volume, NaN vertices, fragmented shell are refused), distinguishes that watertightness only warns, and documents the force override. It stops short of stating where files are written, whether existing files are overwritten, or what is returned.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core action and format list, then the gate behavior. Every sentence carries information, though the gate explanation is dense and could be trimmed slightly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-parameter mutation tool with no output schema and no annotations, the description covers validation/override behavior well but omits the required `name` parameter's meaning, the stl_binary toggle, and any return/output location, leaving gaps an agent must guess at.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It documents formats and force (including the gate override), but leaves the required `name` parameter and `stl_binary` (binary vs ASCII STL) entirely unexplained, so it only partially closes the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (export) and resource (the model) and names the concrete output formats (STL for printing/slicing, OBJ+MTL for DCC), which is exactly the kind of differentiation needed against siblings like check_mesh and look_at_mesh. An agent can tell what this produces without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains the selection context (downstream tools), the default of both formats, and the force=True override for the validation gate. It does not explicitly route to siblings such as check_mesh as a pre-flight step, but the when-to-use and override conditions are clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_partsA

List the built-in parametric parts and their tunable parameters (with defaults and types).

Call this before modelling to get the exact parameter names -- guessing one raises unknown_parameter.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the load and does disclose a real failure mode (guessing a parameter raises unknown_parameter) and the shape of the return (parameter names with defaults and types). It is implicitly a safe read, though it never says so outright.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, zero waste, with the primary action first and the workflow hint second. Nothing padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter discovery tool with no output schema, the description covers what it returns and why to call it before modelling. An agent has everything needed to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. The description adds that the listed entries expose tunable parameters with defaults and types, which is useful context about what the caller gets back.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (List) and resource (built-in parametric parts) plus scope (tunable parameters with defaults and types). It is clearly a discovery tool, distinct from the make_part/make_lattice siblings that create geometry.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use guidance: 'Call this before modelling to get the exact parameter names.' It even states the consequence of skipping it (unknown_parameter). No explicit when-not or named alternative, but the context is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

look_at_meshB

Render the model and return the image directly to you -- this is the core of self-verification.

The render comes back as image content, so you can actually see what you built and decide what to change.

views: 'four' (a 2x2 montage; start here) / 'single' (one viewpoint) / 'grid' (six). turntable_frames>0 also writes a turntable GIF for the human, and returns its path. Returns [image, ...] followed by a JSON summary -- the image always comes first.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
sizeNo
viewsNofour
azimuthNo
shadingNophong
elevationNo
wireframeNo
turntable_framesNo

TDQS

B3.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does well: it discloses that the render returns image content, that the image always precedes the JSON summary, and that turntable_frames>0 writes a GIF file for the human and returns its path. It does not cover error behavior or mesh-missing cases, but the key side effect and return ordering are explicit.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core render-and-return purpose is front-loaded with bold emphasis, and the remaining guidance is compact. The structure is easy to scan, though slightly informal.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a render tool with no output schema and no annotations, the description adequately explains the return format and two parameters. However, six of eight parameters have no semantics anywhere, and there is no mention of prerequisites or failure modes, so it is only minimally sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across 8 parameters, so the description must compensate. It only explains views ('four'/'single'/'grid') and turntable_frames behavior; name, size, azimuth, shading, elevation, and wireframe remain entirely undocumented, leaving most parameters opaque.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (render) and resource (model), and frames the purpose as self-verification. It does not explicitly differentiate from sibling check_mesh or describe_model, but the action is unambiguous without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description recommends 'views: four' as the starting point, implying primary usage. However, it gives no explicit when-not condition or comparison to siblings like check_mesh, leaving the agent to infer when rendering is preferable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

make_latticeA

Model free-form geometry with signed distance fields: lattices, porous structures, implicit surfaces.

primitive: sphere / box / rounded_box / gyroid / schwarz_p. size_mm: the part's bounding box in mm. A single number means a cube with that edge length, [x, y, z] an arbitrary box; omit it to use cell_mm as a cube. res: samples per axis -- a precision knob, not a dimension. Start at 64; 128 is fine but 8x slower. The resulting voxel edge (size/res) is reported back. wall_mm: wall thickness for shells and lattices; period_mm: lattice period (defaults to cell_mm). lattice: shell (hollow it out) / gyroid_grading (fill the interior with lattice). subtract: corner_notch / corner_notch2 (cut a corner away to expose the interior).

ParametersJSON Schema
NameRequiredDescriptionDefault
resNo
nameNo
cell_mmNo
latticeNo
size_mmNo
wall_mmNo
subtractNo
period_mmNo
primitiveNogyroid

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does disclose useful behavior: res is called out as a precision knob with an 8x-slower cost at 128, and the resulting voxel edge is reported back. However, it says nothing about persistence (whether a part is saved under name), permissions, or what a failure looks like, leaving meaningful gaps for an operation that creates geometry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in one sentence, then parameters are documented as tight labeled lines with no filler. Every line adds a fact an agent needs (defaults, units, speed tradeoff) rather than restating the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter, zero-annotation, no-output-schema tool, the description covers parameters well but leaves the agent unsure what the call produces (a stored part? geometry?) and what 'name' does. It is adequate but not fully self-sufficient for a tool this complex.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and parameter titles are bare, so the description is the sole source of meaning, and it delivers: primitive enumerations, the number-vs-[x,y,z] polymorphism of size_mm, the size/res voxel relationship, and the period_mm-defaults-to-cell_mm coupling. Only 'name' goes unexplained, which is a minor omission against otherwise excellent compensation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line states a specific verb+resource: 'Model free-form geometry with signed distance fields: lattices, porous structures, implicit surfaces.' An agent can tell this is the geometry-generation tool, distinct from sibling utilities like check_mesh or describe_model. It does not explicitly contrast with make_part, which is the most likely point of confusion, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives operational guidance ('Start at 64', 'omit it to use cell_mm as a cube') but never says when to choose this tool over make_part or when a lattice vs. shell is appropriate. Usage is implied through parameter notes rather than stated selection criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

make_partB

Build a part from the built-in parametric library (gear / vase / torus knot / lattice coupon).

Returns a topology verdict. See list_parts for valid part values; an unknown key in params is reported explicitly rather than silently ignored. The model is stored in the session and referenced by name from the other tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNo
partYes
paramsNo

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does disclose some behavior: it returns a topology verdict, unknown `params` keys are reported explicitly rather than silently ignored, and the result is session-stored. It omits anything about failure modes, auth, or whether the build consumes resources, so it is only partially transparent for a mutation-style tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the core action followed by return value and session semantics. No filler, though the parenthetical example list is slightly dense.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema and no annotations, so the description must do more work. It covers the return type at a high level (topology verdict) and the session model, but leaves `params` structure and error behavior unspecified for a 3-parameter build tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate. It does add meaning for each of the three params — `part` values come from list_parts, `name` is the session handle used by other tools, and `params` rejects unknown keys — but it never describes the shape or expected keys of `params`, leaving the main data-carrying parameter under-specified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Build a part from the built-in parametric library') and enumerates concrete examples (gear / vase / torus knot / lattice coupon). It does not explicitly differentiate itself from the sibling make_lattice, which is the closest overlap, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Routes the agent to list_parts for valid `part` values and notes the model can be referenced by other tools, which implies downstream usage. However there is no explicit when-to-use vs make_lattice, and no statement of prerequisites or when-not to call it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 7 tool updatesv0.1.0
    • First observedcheck_mesh
    • First observeddescribe_model
    • First observedexport_mesh
    • First observedlist_parts
    • First observedlook_at_mesh
    • First observedmake_lattice
    • First observedmake_part

TDQS

A3.7/5.0

Scored across 7 tools

Disambiguation4/5

The three build/inspect verbs are well-separated: make_part (library parts) vs make_lattice (free-form SDF) vs list_parts (discovery). check_mesh, look_at_mesh, and describe_model overlap somewhat (all report topology/bbox), but their descriptions clearly split numeric evidence, visual rendering, and parameter recall.

Naming Consistency4/5

Nearly all names follow a verb_object snake_case pattern (list_parts, make_part, check_mesh, export_mesh, describe_model). The sole deviation is look_at_mesh, which inserts a preposition but remains easily readable.

Tool Count5/5

Seven tools is well-scoped for a model-build-inspect-export workflow; each tool covers a distinct stage and none feels redundant or padded.

Completeness4/5

Covers discovery, two build paths, numeric and visual verification, export, and parameter recall. Minor gaps: no delete_model, no list_models to enumerate session models, and no pure edit/parameter-update tool, though rebuilding via make_part/make_lattice can work around these.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables LLMs to safely generate parametric CAD parts (STEP/STL) using verified templates and FreeCAD, with validation and assembly support.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Exposes OpenSCAD CLI as MCP tools for validating, rendering, and exporting parametric 3D models. Enables LLM clients to interactively create and manipulate OpenSCAD designs.
    4
    MIT