openscad-mcp 0.6.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openscad_mcp-0.6.1/.claude-plugin/marketplace.json +15 -0
- openscad_mcp-0.6.1/.claude-plugin/plugin.json +22 -0
- openscad_mcp-0.6.1/.gitignore +199 -0
- openscad_mcp-0.6.1/AGENTS.md +56 -0
- openscad_mcp-0.6.1/LICENSE +21 -0
- openscad_mcp-0.6.1/PKG-INFO +559 -0
- openscad_mcp-0.6.1/README.md +509 -0
- openscad_mcp-0.6.1/evals/README.md +158 -0
- openscad_mcp-0.6.1/mcp-config.json +8 -0
- openscad_mcp-0.6.1/pyproject.toml +189 -0
- openscad_mcp-0.6.1/skills/openscad-design/SKILL.md +283 -0
- openscad_mcp-0.6.1/src/openscad_mcp/__init__.py +28 -0
- openscad_mcp-0.6.1/src/openscad_mcp/__main__.py +11 -0
- openscad_mcp-0.6.1/src/openscad_mcp/analysis.py +1574 -0
- openscad_mcp-0.6.1/src/openscad_mcp/assembly.py +614 -0
- openscad_mcp-0.6.1/src/openscad_mcp/camera.py +766 -0
- openscad_mcp-0.6.1/src/openscad_mcp/checks.py +837 -0
- openscad_mcp-0.6.1/src/openscad_mcp/csgfeatures.py +1426 -0
- openscad_mcp-0.6.1/src/openscad_mcp/diagnostics.py +472 -0
- openscad_mcp-0.6.1/src/openscad_mcp/geom.py +2395 -0
- openscad_mcp-0.6.1/src/openscad_mcp/massprops.py +631 -0
- openscad_mcp-0.6.1/src/openscad_mcp/mesh.py +943 -0
- openscad_mcp-0.6.1/src/openscad_mcp/parts/28byj-48.scad +229 -0
- openscad_mcp-0.6.1/src/openscad_mcp/parts/README.md +114 -0
- openscad_mcp-0.6.1/src/openscad_mcp/parts/kw11-3z.scad +196 -0
- openscad_mcp-0.6.1/src/openscad_mcp/parts/lazy-susan-4in.scad +189 -0
- openscad_mcp-0.6.1/src/openscad_mcp/parts/nema17.scad +196 -0
- openscad_mcp-0.6.1/src/openscad_mcp/parts/tcrt5000-module.scad +179 -0
- openscad_mcp-0.6.1/src/openscad_mcp/parts_catalog.py +1136 -0
- openscad_mcp-0.6.1/src/openscad_mcp/printability.py +1557 -0
- openscad_mcp-0.6.1/src/openscad_mcp/reference.py +1666 -0
- openscad_mcp-0.6.1/src/openscad_mcp/server.py +5154 -0
- openscad_mcp-0.6.1/src/openscad_mcp/threemf.py +710 -0
- openscad_mcp-0.6.1/src/openscad_mcp/types.py +201 -0
- openscad_mcp-0.6.1/src/openscad_mcp/utils/__init__.py +7 -0
- openscad_mcp-0.6.1/src/openscad_mcp/utils/config.py +445 -0
- openscad_mcp-0.6.1/src/openscad_mcp/wrappers.py +491 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openscad-mcp",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Coop",
|
|
5
|
+
"url": "https://github.com/robertcoop"
|
|
6
|
+
},
|
|
7
|
+
"description": "OpenSCAD tooling for AI assistants: rendering, measurement, validation, and export, plus a design skill for 3D-printable parts.",
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "openscad-mcp",
|
|
11
|
+
"source": "./",
|
|
12
|
+
"description": "OpenSCAD rendering, measurement, validation, and export as MCP tools, plus the openscad-design skill."
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "openscad-mcp",
|
|
4
|
+
"displayName": "OpenSCAD MCP",
|
|
5
|
+
"version": "0.6.1",
|
|
6
|
+
"description": "Render, measure, validate, and export OpenSCAD models from Claude Code, with a skill that teaches the measure-before-you-look design loop for 3D-printable parts.",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Coop",
|
|
9
|
+
"url": "https://github.com/robertcoop"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/robertcoop/openscad-mcp#readme",
|
|
12
|
+
"repository": "https://github.com/robertcoop/openscad-mcp",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"openscad",
|
|
16
|
+
"cad",
|
|
17
|
+
"3d-printing",
|
|
18
|
+
"mcp",
|
|
19
|
+
"rendering"
|
|
20
|
+
],
|
|
21
|
+
"mcpServers": "./mcp-config.json"
|
|
22
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
# ...but src/openscad_mcp/parts/ is the purchased-parts catalog, not a build
|
|
21
|
+
# artifact. It is package data that parts_catalog.py reads at runtime, so it
|
|
22
|
+
# must be tracked and must ship in the wheel.
|
|
23
|
+
!src/openscad_mcp/parts/
|
|
24
|
+
sdist/
|
|
25
|
+
var/
|
|
26
|
+
wheels/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
|
|
33
|
+
# PyInstaller
|
|
34
|
+
# Usually these files are written by a python script from a template
|
|
35
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
36
|
+
*.manifest
|
|
37
|
+
*.spec
|
|
38
|
+
|
|
39
|
+
# Installer logs
|
|
40
|
+
pip-log.txt
|
|
41
|
+
pip-delete-this-directory.txt
|
|
42
|
+
|
|
43
|
+
# Unit test / coverage reports
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.nox/
|
|
47
|
+
.coverage
|
|
48
|
+
.coverage.*
|
|
49
|
+
.cache
|
|
50
|
+
nosetests.xml
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.cover
|
|
53
|
+
*.py,cover
|
|
54
|
+
.hypothesis/
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
cover/
|
|
57
|
+
|
|
58
|
+
# Translations
|
|
59
|
+
*.mo
|
|
60
|
+
*.pot
|
|
61
|
+
|
|
62
|
+
# Django stuff:
|
|
63
|
+
*.log
|
|
64
|
+
local_settings.py
|
|
65
|
+
db.sqlite3
|
|
66
|
+
db.sqlite3-journal
|
|
67
|
+
|
|
68
|
+
# Flask stuff:
|
|
69
|
+
instance/
|
|
70
|
+
.webassets-cache
|
|
71
|
+
|
|
72
|
+
# Scrapy stuff:
|
|
73
|
+
.scrapy
|
|
74
|
+
|
|
75
|
+
# Sphinx documentation
|
|
76
|
+
docs/_build/
|
|
77
|
+
|
|
78
|
+
# PyBuilder
|
|
79
|
+
.pybuilder/
|
|
80
|
+
target/
|
|
81
|
+
|
|
82
|
+
# Jupyter Notebook
|
|
83
|
+
.ipynb_checkpoints
|
|
84
|
+
|
|
85
|
+
# IPython
|
|
86
|
+
profile_default/
|
|
87
|
+
ipython_config.py
|
|
88
|
+
|
|
89
|
+
# pyenv
|
|
90
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
91
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
92
|
+
.python-version
|
|
93
|
+
|
|
94
|
+
# pipenv
|
|
95
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
96
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
97
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
98
|
+
# install all needed dependencies.
|
|
99
|
+
#Pipfile.lock
|
|
100
|
+
|
|
101
|
+
# poetry
|
|
102
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
103
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
104
|
+
# commonly ignored for libraries.
|
|
105
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
106
|
+
#poetry.lock
|
|
107
|
+
|
|
108
|
+
# pdm
|
|
109
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
110
|
+
#pdm.lock
|
|
111
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
112
|
+
# in version control.
|
|
113
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
114
|
+
.pdm.toml
|
|
115
|
+
|
|
116
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
117
|
+
__pypackages__/
|
|
118
|
+
|
|
119
|
+
# Celery stuff
|
|
120
|
+
celerybeat-schedule
|
|
121
|
+
celerybeat.pid
|
|
122
|
+
|
|
123
|
+
# SageMath parsed files
|
|
124
|
+
*.sage.py
|
|
125
|
+
|
|
126
|
+
# Environments
|
|
127
|
+
.env
|
|
128
|
+
.venv
|
|
129
|
+
env/
|
|
130
|
+
venv/
|
|
131
|
+
ENV/
|
|
132
|
+
env.bak/
|
|
133
|
+
venv.bak/
|
|
134
|
+
test-env/
|
|
135
|
+
|
|
136
|
+
# Spyder project settings
|
|
137
|
+
.spyderproject
|
|
138
|
+
.spyproject
|
|
139
|
+
|
|
140
|
+
# Rope project settings
|
|
141
|
+
.ropeproject
|
|
142
|
+
|
|
143
|
+
# mkdocs documentation
|
|
144
|
+
/site
|
|
145
|
+
|
|
146
|
+
# mypy
|
|
147
|
+
.mypy_cache/
|
|
148
|
+
.dmypy.json
|
|
149
|
+
dmypy.json
|
|
150
|
+
|
|
151
|
+
# Pyre type checker
|
|
152
|
+
.pyre/
|
|
153
|
+
|
|
154
|
+
# pytype static type analyzer
|
|
155
|
+
.pytype/
|
|
156
|
+
|
|
157
|
+
# Cython debug symbols
|
|
158
|
+
cython_debug/
|
|
159
|
+
|
|
160
|
+
# PyCharm
|
|
161
|
+
.idea/
|
|
162
|
+
|
|
163
|
+
# VS Code
|
|
164
|
+
.vscode/
|
|
165
|
+
*.code-workspace
|
|
166
|
+
|
|
167
|
+
# macOS
|
|
168
|
+
.DS_Store
|
|
169
|
+
|
|
170
|
+
# Windows
|
|
171
|
+
Thumbs.db
|
|
172
|
+
ehthumbs.db
|
|
173
|
+
|
|
174
|
+
# OpenSCAD specific
|
|
175
|
+
*.scad.bak
|
|
176
|
+
*.stl
|
|
177
|
+
*.3mf
|
|
178
|
+
*.amf
|
|
179
|
+
*.dxf
|
|
180
|
+
*.svg
|
|
181
|
+
*.csg
|
|
182
|
+
renders/
|
|
183
|
+
temp/
|
|
184
|
+
|
|
185
|
+
# Project specific
|
|
186
|
+
/tmp/
|
|
187
|
+
/temp/
|
|
188
|
+
/cache/
|
|
189
|
+
*.png
|
|
190
|
+
*.jpg
|
|
191
|
+
*.jpeg
|
|
192
|
+
*.gif
|
|
193
|
+
!docs/images/**
|
|
194
|
+
test_output/
|
|
195
|
+
# Claude Code / Impeccable local caches
|
|
196
|
+
.impeccable/
|
|
197
|
+
|
|
198
|
+
# Local research notes (not published)
|
|
199
|
+
docs/research/
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Agent brief for the OpenSCAD MCP server. Claude Code should read the richer
|
|
4
|
+
`skills/openscad-design/SKILL.md` instead.
|
|
5
|
+
|
|
6
|
+
A Model Context Protocol server wrapping the OpenSCAD CLI: it renders `.scad` source
|
|
7
|
+
to images, exports meshes, and returns exact geometric measurements. OpenSCAD must be
|
|
8
|
+
installed on the host. Everything is in millimetres; OpenSCAD itself is unitless.
|
|
9
|
+
|
|
10
|
+
## Tools
|
|
11
|
+
|
|
12
|
+
| Tool | What it does |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `render` | Images. `mode=views\|section\|parts\|compare`. `grounded=true` gives an orthographic render with a stated mm/px scale and drawn annotations. |
|
|
15
|
+
| `measure` | Exact numbers from the exported mesh. `mode=model\|parts\|section\|mass\|probe\|features\|printability\|orientation\|anchors`: bounding box, volume, area, component count, watertightness, point-in-solid probes, holes read off the CSG tree. Also takes an existing STL via `mesh`. |
|
|
16
|
+
| `check` | Relations between named parts of an assembly. `mode=interference\|clearance\|contact\|alignment\|motion\|rules`. Each part is exported separately, so identity survives; `mode=rules` runs a YAML check file and returns an exit code. |
|
|
17
|
+
| `validate` | `mode=syntax\|geometry\|predicates\|includes\|printability`. Diagnostics parsed from stderr, never from the exit code. |
|
|
18
|
+
| `scad_eval` | Evaluates expressions in the design's own parameter space, returning typed values. |
|
|
19
|
+
| `reference` | Shipped engineering data: fits, fasteners, inserts, bearings, magnets, joints, parts, conventions, cheatsheet, dfm, materials. |
|
|
20
|
+
| `export_model` | STL, 3MF, AMF, OFF, NEF3, DXF, SVG, PDF, CSG. `parts=` writes one named-object 3MF. |
|
|
21
|
+
| `check_openscad` | Binary presence, version, capabilities. |
|
|
22
|
+
| `get_libraries` | Libraries installed on this machine, with the exact import line for each. |
|
|
23
|
+
| `get_project_files` | `.scad` files in a directory and their include/use dependency graph. `mode=trace` follows a constant's dependents. |
|
|
24
|
+
| `clear_cache` | Drops the render cache. |
|
|
25
|
+
| `model` | Workspace file CRUD: `action=create\|get\|update\|list\|delete`. `template="part:<id>"` writes a catalogued purchased part. |
|
|
26
|
+
|
|
27
|
+
## The design loop
|
|
28
|
+
|
|
29
|
+
1. State units, orientation, datum, and print process first. They are invisible in the source and wrong in half of all first attempts.
|
|
30
|
+
2. Declare every meaningful dimension as a named variable at the top; derive the rest with expressions.
|
|
31
|
+
3. Run `validate(mode="syntax")` before anything else. OpenSCAD exits 0 on failed asserts, unknown modules, and missing includes, so the exit code proves nothing.
|
|
32
|
+
4. Run `measure` before trusting any picture. Numbers decide, pictures confirm; a vision model reads broken geometry as fine.
|
|
33
|
+
5. Then `render(grounded=true)` with one to three views. More images make counting and comparison worse, not better.
|
|
34
|
+
6. For assemblies: one module per part, overlap coplanar faces by an epsilon, and take clearances from `reference(topic="fits")` rather than memory. Never union the assembly.
|
|
35
|
+
Then hand the parts to `check` by name: `interference` and `clearance` for fit, `contact` for what should touch, `alignment` for a hole that is off by half a millimetre (interference and clearance both read zero there), `motion` for a sweep, `mass` for weight, centre-of-mass and inertia limits. Every row carries `quality.fn`; a distance inside the tessellation error bound comes back `UNRESOLVED`, so re-run at higher `$fn` rather than believing it. `measure(mode="features")` lists the holes a part cuts, and `reference(topic="parts")` plus `model(action="create", template="part:<id>")` gives you a sourced purchased part with named anchors and a clearance mask instead of guessed dimensions. Freeze the rules in a check file and re-run `check(mode="rules")` after every edit.
|
|
36
|
+
7. Confirm `measure(mode="parts")` reports the component count you designed, and that `validate(mode="geometry")` returns `mesh_health.manifold == true`. A `null` there means the check did not run, not that it passed.
|
|
37
|
+
8. Iterate by changing one variable and re-measuring. Use `scad_eval` to check derived expressions before spending a render.
|
|
38
|
+
9. On every response read `errors`, then `warnings`, then `hints`, then the payload. Check `cached`: a cached result can predate an edit to an included file.
|
|
39
|
+
10. `export_model` last, once the numbers and the manifold check pass.
|
|
40
|
+
|
|
41
|
+
## Reading a render
|
|
42
|
+
|
|
43
|
+
The text digest before the image carries the camera eye/center/up, the projection, the
|
|
44
|
+
bounding box, and the scale in mm per pixel. That scale is valid only under
|
|
45
|
+
orthographic projection, which is what `grounded=true` selects. Auto-framed
|
|
46
|
+
(`--viewall`) renders fit the model to the frame, so absolute scale is unknown and a
|
|
47
|
+
2 mm cube looks identical to a 500 mm one.
|
|
48
|
+
|
|
49
|
+
## Gotchas
|
|
50
|
+
|
|
51
|
+
Variables are compile-time and scope-final, so assigning inside an `if` or `for` does
|
|
52
|
+
not change the outer value; use `x = cond ? a : b`. `difference()` subtracts every
|
|
53
|
+
child after the first, so order changes the result. 2D and 3D geometry cannot be
|
|
54
|
+
combined without extruding first. A global `$fn` applies to every curve and makes
|
|
55
|
+
exports slow and huge. `import()` needs `convexity=10` or it renders with holes.
|
|
56
|
+
Solids that touch exactly, and any zero-thickness wall, produce non-manifold output.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 quellant
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|