io.github.emirmrkaljevic/mesh2cad·Media

mesh2cad

Mesh (STL/OBJ/PLY) to editable STEP CAD: fitted planes, cylinders, spheres, cones + fidelity report

Stars
0
Version
0.4.2
License
MIT
Review
Passed
Language
Python
Last commit
1h ago
Forks
0
Open issues
1
Registry update
Oct 17, 2026
3d-printingcadmcpmcp-serveropencascadepythonreverse-engineeringstepstl

Connect

pypi
Terminal
$claude mcp add mesh2cad \  -- uvx mesh2cad==0.4.2

Environment variables

Nothing to configure — it works as is.

Security review

Review passed

Reviewed 1h ago.

metadata
scanned
packages
1 checked

No findings.

Tools

This server runs on your machine, so its tool list is not fetched before install — the README below describes what it offers.

mesh2cad

ci emirmrkaljevic/mesh2cad MCP server

Support my open-source work: get a SkinToScan pack
If you find this useful, you can support me by getting a SkinToScan pack, my other project. It's a one-time purchase that directly funds my open-source work.

Buy Single Picture Check, €4.49 Buy 5 Picture Pack, €19.99 (popular) Buy 15 Picture Pack, €54.99

Turn 3D meshes (STL/OBJ/PLY) into real, editable CAD (STEP) — fitted planes, cylinders, spheres, and cones instead of triangle soup. CLI + MCP server for AI agents. MIT, pure Python on top of OpenCascade.

demo

before/after

Left: 21k-triangle mesh with 0.1 mm noise. Right: the STEP mesh2cad writes — 10 planes + 2 cylinders, max deviation 0.05 mm from the true part.

Install

pip install mesh2cad              # CLI + STEP
pip install "mesh2cad[mcp]"       # + MCP server
pip install "mesh2cad[cadquery]"  # + run/verify generated CadQuery scripts

Use

mesh2cad convert part.stl -o part.step --tol 0.1 --report report.md
mesh2cad report part.stl --json

--tol ≈ the noise / accuracy of your mesh in model units (default 0.1 % of the bounding-box diagonal).

Output: STEP + fidelity report — method (exact / faceted-hybrid / faceted), face types, solid validity, two-sided Hausdorff / RMS deviation, volume error, % area per surface type.

Editable parametric CadQuery script

For simple prismatic parts mesh2cad can also write a CadQuery Python script where every dimension is a named parameter, so you change a number and re-run instead of editing B-rep faces:

mesh2cad convert bracket.stl --cadquery bracket.py   # add -o bracket.step to also write STEP
python bracket.py                                    # -> bracket.step
# --- parameters ---
base_x           = 40.0  # base length (X)
base_y           = 30.0  # base width (Y)
base_t           = 6.0  # base thickness (Z)
...
hole_1_d         = 6.0  # diameter
hole_1_x         = 0.0
hole_1_y         = 0.0
hole_1_csk_d     = 12.0  # countersink diameter
hole_1_csk_angle = 90.0  # countersink angle

Make the hole 2 mm bigger: set hole_1_d = 8.0 and run it again.

Supported: Z-up box/plate base, rectangular pads on the top face, vertical holes (through, blind, countersunk) and vertical bosses (straight, drafted, chamfered). Values are rounded to clean numbers (5.997 → 6.0) when that stays within --tol. Before writing, the script is executed and compared with the mesh (needs cadquery installed); if the part is outside this vocabulary or the script deviates, nothing is written and the command exits with code 2 and the reason. Use STEP output for everything else.

MCP server (Claude Desktop, Cursor, …)

{ "mcpServers": { "mesh2cad": { "command": "mesh2cad", "args": ["mcp"] } } }

Tools: convert(input_path, output_path?, tol?, cylinders?, spheres?, cones?), analyze(input_path, tol?, cylinders?, spheres?, cones?), report(input_path, tol?, cylinders?, spheres?, cones?), to_cadquery(input_path, output_path?, tol?) (returns the parameters and measured deviation). spheres and cones default on.

Listed on Glama and the TensorBlock MCP catalog.

How it works

  1. Load + clean mesh (trimesh); Taubin-smoothed copy for normals.
  2. Region growing into planes; strip chains and leftovers are fitted as cylinders, spheres, or cones (normal-ray / algebraic / constant-normal-angle fits, robust nonlinear refinement). A coaxial cylinder + cone component (countersink, chamfered pin) is split by normal-to-axis angle first. Small noisy patches that tile a sphere are merged into one spherical region. Split/redundant regions are merged and absorbed.
  3. Exact B-rep (OCP / OpenCascade): plane polygons from triple-plane intersections, circles from plane ∩ cylinder and plane ∩ sphere, plane ∩ cone, cylindrical faces, spherical caps and zones, conical frustums, sewn into a solid and validated.
  4. Fallbacks: hybrid faceted solid (vertices snapped to fitted surfaces, coplanar triangles merged), then raw faceted solid.
  5. Fidelity report against the input mesh.

Self-test benchmark

Ground-truth cadquery parts → tessellated + subdivided (≤ 2 mm edges) → Gaussian vertex noise → mesh2cad → compared with the truth. 3 seeds, worst case shown (python benchmarks/run.py); the same cases run in CI.

partnoise σ (mm)exactfaces (plane/cyl[/sph[/cone]])Hausdorff (mm)RMS (mm)vol err %time s
cube0.03/36/00.0000.0000.000.6
cube0.053/36/00.0190.0060.070.6
cube0.23/36/00.0750.0260.340.6
l_bracket0.03/38/00.0000.0000.000.9
l_bracket0.053/38/00.0400.0060.030.9
l_bracket0.23/38/00.2160.0260.150.8
stepped_block0.03/311/00.0000.0000.002.0
stepped_block0.053/311/00.0320.0060.022.0
stepped_block0.23/311/00.1360.0220.071.9
plate_with_hole0.03/36/10.0150.0020.001.7
plate_with_hole0.053/36/10.0280.0060.051.9
plate_with_hole0.23/36/10.0780.0230.181.6
boss0.03/37/10.0270.0060.011.8
boss0.053/37/10.0330.0070.032.0
boss0.23/37/10.0900.0170.073.3
ribbed_plate0.03/310/20.0130.0020.002.2
ribbed_plate0.053/310/20.0280.0050.032.1
ribbed_plate0.23/310/20.1080.0200.122.1
spherical_dome0.03/36/0/10.0650.0070.041.6
spherical_dome0.053/36/0/10.0670.0080.061.6
spherical_dome0.23/36/0/10.2410.0280.251.7
spherical_pocket0.03/36/0/10.0460.0040.021.9
spherical_pocket0.053/36/0/10.0570.0050.031.9
spherical_pocket0.23/36/0/10.0750.0150.131.8
countersunk_plate0.03/36/1/0/10.0390.0030.012.0
countersunk_plate0.053/36/1/0/10.0550.0090.072.0
countersunk_plate0.21/316342/0, 6/1/0/10.5900.0490.5421.6
tapered_boss0.03/37/0/0/10.0860.0110.051.6
tapered_boss0.053/37/0/0/10.0950.0120.051.6
tapered_boss0.23/37/0/0/10.1230.0250.221.5
chamfered_boss0.03/37/1/0/10.0530.0050.022.4
chamfered_boss0.053/37/1/0/10.0960.0120.082.3
chamfered_boss0.23/37/1/0/10.5110.0590.492.1

"exact" = valid B-rep with the true face count. All rows are the worst of 3 seeds from python benchmarks/run.py. A row below 3/3 includes the faceted fallback (face count = triangles); that fallback is a valid solid. The 90° countersink (Ø6 hole, Ø12 head, 3 mm deep) at 0.2 mm noise is exact on 1 of 3 seeds. CI checks the cases that are exact, plus one fallback that must stay a valid solid.

Limits (v0.4.2)

Planes, cylinders, spheres, and cones. Cylinders must end on planes perpendicular to their axis (through-holes, bosses, pins). Spheres are exact when each boundary loop is a single plane ∩ sphere circle (domes, spherical bosses, spherical pockets, and zones between two parallel planes). A sphere that runs into an edge or a cylinder falls back to the hybrid solid — vertices are still snapped onto the fitted sphere, but the face stays faceted. Cones are exact when they run between two planes perpendicular to their axis: countersinks, chamfered holes and bosses, drafted/tapered bosses. Cones may also meet a coaxial cylinder (countersunk hole, chamfered pin). A cone that ends in its apex falls back to the hybrid solid. Countersinks and narrow chamfers are exact through about 0.05 mm of noise; a 2 mm chamfer and a wide drafted wall stay exact at 0.2 mm. The countersunk plate's 90° countersink (Ø6 hole, Ø12 head, 3 mm deep) at 0.2 mm noise is exact on one of three seeds. On the other two the conical band and the hole wall meet at a single vertex, so those seeds fall back to a valid faceted solid. No fillets or free-form yet.

Development

pip install -e .[dev]
pytest
python docs/make_demo.py   # regenerate images (needs matplotlib, pillow)

License: MIT

From the server's repository — third-party content, read it before running anything it suggests.

More in Media

Listing from the official MCP Registry · repository facts from GitHub, 1h ago.