b123d-print 0.1.0__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.
Files changed (31) hide show
  1. b123d_print-0.1.0/LICENSE +21 -0
  2. b123d_print-0.1.0/PKG-INFO +501 -0
  3. b123d_print-0.1.0/README.md +485 -0
  4. b123d_print-0.1.0/THIRD_PARTY.md +37 -0
  5. b123d_print-0.1.0/pyproject.toml +51 -0
  6. b123d_print-0.1.0/pyproject.toml.orig +34 -0
  7. b123d_print-0.1.0/src/b123d_print/__init__.py +16 -0
  8. b123d_print-0.1.0/src/b123d_print/checks.py +391 -0
  9. b123d_print-0.1.0/src/b123d_print/cli.py +251 -0
  10. b123d_print-0.1.0/src/b123d_print/config.py +191 -0
  11. b123d_print-0.1.0/src/b123d_print/data/AGENTS.md +110 -0
  12. b123d_print-0.1.0/src/b123d_print/data/b123d-print.toml +13 -0
  13. b123d_print-0.1.0/src/b123d_print/data/materials/abs.toml +2 -0
  14. b123d_print-0.1.0/src/b123d_print/data/materials/asa.toml +2 -0
  15. b123d_print-0.1.0/src/b123d_print/data/materials/petg.toml +2 -0
  16. b123d_print-0.1.0/src/b123d_print/data/materials/pla.toml +2 -0
  17. b123d_print-0.1.0/src/b123d_print/data/materials/tpu.toml +2 -0
  18. b123d_print-0.1.0/src/b123d_print/data/part_template.py +63 -0
  19. b123d_print-0.1.0/src/b123d_print/data/printers_orca.json +3652 -0
  20. b123d_print-0.1.0/src/b123d_print/examples/esp_ir_blaster/esp_ir_blaster.py +361 -0
  21. b123d_print-0.1.0/src/b123d_print/examples/hole_coupon/hole_coupon.py +78 -0
  22. b123d_print-0.1.0/src/b123d_print/examples/plate_2gang_to_1gang/plate_2gang_to_1gang.py +252 -0
  23. b123d_print-0.1.0/src/b123d_print/examples/testblock/testblock.py +48 -0
  24. b123d_print-0.1.0/src/b123d_print/examples/tuya_motion_sensor_outdoor_case/tuya_motion_sensor_outdoor_case.py +393 -0
  25. b123d_print-0.1.0/src/b123d_print/mcp_server.py +364 -0
  26. b123d_print-0.1.0/src/b123d_print/profiles.py +203 -0
  27. b123d_print-0.1.0/src/b123d_print/project.py +208 -0
  28. b123d_print-0.1.0/src/b123d_print/py.typed +0 -0
  29. b123d_print-0.1.0/src/b123d_print/render.py +226 -0
  30. b123d_print-0.1.0/src/b123d_print/sandbox.py +163 -0
  31. b123d_print-0.1.0/src/b123d_print/worker.py +220 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 KD5RYN and contributors
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.
@@ -0,0 +1,501 @@
1
+ Metadata-Version: 2.4
2
+ Name: b123d-print
3
+ Version: 0.1.0
4
+ Summary: Design, check and export 3D-printable parts with build123d, from any LLM via MCP
5
+ Keywords: cad,3d-printing,build123d,mcp,llm
6
+ Author: KD5RYN, b123d-print contributors
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ License-File: THIRD_PARTY.md
10
+ Requires-Dist: build123d==0.13.0
11
+ Requires-Dist: matplotlib==3.11.2
12
+ Requires-Dist: mcp==2.3.0
13
+ Requires-Dist: numpy==2.5.4
14
+ Requires-Python: ==3.12.*
15
+ Description-Content-Type: text/markdown
16
+
17
+ # b123d-print
18
+
19
+ [![CI](https://github.com/KD5RYN/b123d-print/actions/workflows/ci.yml/badge.svg)](https://github.com/KD5RYN/b123d-print/actions/workflows/ci.yml)
20
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
21
+
22
+ Design 3D-printable parts with an AI assistant — any assistant. You describe a part (photos,
23
+ caliper readings); the model writes a [build123d](https://github.com/gumyr/build123d) Python
24
+ script; b123d-print builds it in a sandbox, renders it, cuts cross-sections, runs print checks,
25
+ and exports the formats you pick: STEP, STL, 3MF, PNG.
26
+
27
+ | | |
28
+ | --- | --- |
29
+ | ![ESP IR blaster box, assembly with board stand-ins](docs/images/esp_ir_blaster_assembly.png) | ![Section through the LED: base, lid, board and LED stand-ins](docs/images/esp_ir_blaster_xz0.6.png) |
30
+ | *Two-part box for an IR transceiver board, with the board stand-ins* | *Section through the LED: lid cone, board, clearances* |
31
+ | ![Outdoor motion sensor case, arch cover](docs/images/tuya_motion_sensor_outdoor_case_arch_assembly.png) | ![Wall plate section through the counterbores](docs/images/plate_2gang_to_1gang_xy2.45.png) |
32
+ | *Outdoor sensor case: dovetail back plate + support-free cover* | *Wall plate: section just past the face, where the wall check looks* |
33
+
34
+ **Scope: parts for FDM 3D printing, nothing else.** Everything is built around a printed part:
35
+ print orientation with the bed at Z=0, hole compensation, mating clearance, and overhang,
36
+ wall-thickness and overlap checks. It is not a general CAD framework.
37
+
38
+ ## What's in it
39
+
40
+ - **`b123d-print` package + CLI** — export, 6-view renders, colored multi-part cross-sections,
41
+ and print checks: **overlap** between mating parts, **overhang** in print orientation, **wall
42
+ thickness** around cuts, **probes** ("is the roof closed?").
43
+ - **`b123d-print-mcp`** — an [MCP](https://modelcontextprotocol.io) server, so it works from
44
+ Claude Desktop, Claude Code, Gemini CLI, Cursor, VS Code, Codex and other MCP clients. Part
45
+ scripts are model-written code: they run in a sandbox with a timeout and no network.
46
+ - **The workflow, model-neutral** — MCP prompts plus an [`AGENTS.md`](AGENTS.md) any agent can
47
+ read. The rules come from real prints; one exists because a part broke through a 0.05 mm skin
48
+ that no render showed.
49
+ - **Examples** — four parts designed this way and printed, plus a hole calibration coupon, in
50
+ [`src/b123d_print/examples/`](src/b123d_print/examples/).
51
+
52
+ ## Quick start
53
+
54
+ 1. Install [uv](https://docs.astral.sh/uv/getting-started/installation/). It fetches Python 3.12
55
+ and every pinned dependency for you.
56
+ 2. Add the server to your MCP client — Claude Desktop shown, [others below](#connect-your-mcp-client):
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "b123d-print": {
62
+ "command": "uvx",
63
+ "args": ["--from", "git+https://github.com/KD5RYN/b123d-print", "b123d-print-mcp",
64
+ "--workspace", "/Users/you/parts"]
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ 3. Restart the client and ask: *"Design a 40 mm round spacer, 10 mm tall, with an M3 hole."*
71
+ Files land in `/Users/you/parts/parts/<name>/out/`.
72
+
73
+ The first start downloads a few hundred MB (mostly OpenCascade, the geometry kernel) and can
74
+ take a few minutes; later starts are fast. See [Troubleshooting](#troubleshooting) if your
75
+ client gives up waiting.
76
+
77
+ > **Images:** `render_preview` and `section` return PNG images. The model can only check its own
78
+ > work if your client passes tool-result images to the model. Claude Desktop and Claude Code do;
79
+ > check your client's docs for others. Without it the checks still run, but the model designs
80
+ > blind — look at the PNGs in `out/` yourself.
81
+
82
+ ## Ways to use it
83
+
84
+ | Method | When | Sandbox |
85
+ | --- | --- | --- |
86
+ | **MCP server via `uvx`** | any MCP client; the normal way | yes (macOS; Linux with bubblewrap) |
87
+ | **MCP server in Docker** | strongest isolation, or Windows | the container |
88
+ | **Claude Code plugin** | Claude Code; adds a skill that points at the workflow | yes |
89
+ | **CLI / Python** | agents that run shell commands, or by hand | no — runs your own scripts |
90
+
91
+ ### Connect your MCP client
92
+
93
+ Every client runs the same command:
94
+ `uvx --from git+https://github.com/KD5RYN/b123d-print b123d-print-mcp --workspace <folder>`.
95
+ The **workspace** is the folder that holds your parts (`parts/<name>/<name>.py`); it is created
96
+ if missing. Without `--workspace` the server uses `$B123D_PRINT_WORKSPACE`, else the folder the
97
+ client starts it in.
98
+
99
+ <details>
100
+ <summary><b>Claude Desktop</b></summary>
101
+
102
+ Settings → Developer → Edit Config (`claude_desktop_config.json`): the block in
103
+ [Quick start](#quick-start). Always pass `--workspace`. If the server fails with "uvx not
104
+ found", use the full path from `which uvx` as `command` (desktop apps don't see your shell's
105
+ `PATH`).
106
+ </details>
107
+
108
+ <details>
109
+ <summary><b>Claude Code</b></summary>
110
+
111
+ ```bash
112
+ claude mcp add b123d-print -- uvx --from git+https://github.com/KD5RYN/b123d-print b123d-print-mcp --workspace ~/parts
113
+ ```
114
+
115
+ Or the plugin (MCP server + skill; uses the project folder as workspace), inside Claude Code:
116
+
117
+ ```text
118
+ /plugin marketplace add KD5RYN/b123d-print
119
+ /plugin install b123d-print@b123d-print
120
+ ```
121
+ </details>
122
+
123
+ <details>
124
+ <summary><b>Gemini CLI</b></summary>
125
+
126
+ `~/.gemini/settings.json` (or `.gemini/settings.json` in a project):
127
+
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "b123d-print": {
132
+ "command": "uvx",
133
+ "args": ["--from", "git+https://github.com/KD5RYN/b123d-print", "b123d-print-mcp", "--workspace", "/Users/you/parts"]
134
+ }
135
+ }
136
+ }
137
+ ```
138
+ </details>
139
+
140
+ <details>
141
+ <summary><b>Cursor</b></summary>
142
+
143
+ `~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project: the same `mcpServers` block as for
144
+ Gemini CLI.
145
+ </details>
146
+
147
+ <details>
148
+ <summary><b>VS Code (Copilot agent mode)</b></summary>
149
+
150
+ `.vscode/mcp.json`:
151
+
152
+ ```json
153
+ {
154
+ "servers": {
155
+ "b123d-print": {
156
+ "type": "stdio",
157
+ "command": "uvx",
158
+ "args": ["--from", "git+https://github.com/KD5RYN/b123d-print", "b123d-print-mcp", "--workspace", "${workspaceFolder}"]
159
+ }
160
+ }
161
+ }
162
+ ```
163
+ </details>
164
+
165
+ <details>
166
+ <summary><b>Codex CLI</b></summary>
167
+
168
+ `~/.codex/config.toml`:
169
+
170
+ ```toml
171
+ [mcp_servers.b123d-print]
172
+ command = "uvx"
173
+ args = ["--from", "git+https://github.com/KD5RYN/b123d-print", "b123d-print-mcp", "--workspace", "/Users/you/parts"]
174
+ ```
175
+ </details>
176
+
177
+ <details>
178
+ <summary><b>Other clients</b></summary>
179
+
180
+ Any client that can start a stdio MCP server: command `uvx`, arguments as above. To pin a
181
+ version, use `git+https://github.com/KD5RYN/b123d-print@v0.1.0` once releases exist.
182
+ </details>
183
+
184
+ ### Docker
185
+
186
+ The container is the sandbox: no network, and it sees only the folder you mount.
187
+
188
+ ```bash
189
+ git clone https://github.com/KD5RYN/b123d-print && cd b123d-print
190
+ ```
191
+
192
+ ```bash
193
+ docker build -t b123d-print-mcp .
194
+ ```
195
+
196
+ Then point your client at Docker instead of `uvx` (Claude Desktop shown):
197
+
198
+ ```json
199
+ {
200
+ "mcpServers": {
201
+ "b123d-print": {
202
+ "command": "docker",
203
+ "args": ["run", "-i", "--rm", "--network", "none",
204
+ "-v", "/Users/you/parts:/work", "b123d-print-mcp"]
205
+ }
206
+ }
207
+ }
208
+ ```
209
+
210
+ - `-i` is required (MCP talks over stdin/stdout); `--rm` removes the container afterwards.
211
+ - Your parts and exports appear in the mounted folder (`/Users/you/parts` above). Settings
212
+ (printer, hole compensation) are kept there too, in `.config/b123d-print/`, since the
213
+ container has no lasting home folder.
214
+ - On Linux, add `"--user", "1000:1000"` (your `id -u`:`id -g`) so exported files belong to you.
215
+ - Released images will also be on GitHub's registry as `ghcr.io/kd5ryn/b123d-print-mcp`.
216
+
217
+ ### CLI and Python (no MCP)
218
+
219
+ For agents that run shell commands, and for you:
220
+
221
+ ```bash
222
+ uv tool install git+https://github.com/KD5RYN/b123d-print
223
+ ```
224
+
225
+ ```bash
226
+ b123d-print init ~/parts && cd ~/parts
227
+ ```
228
+
229
+ ```bash
230
+ b123d-print new bracket
231
+ ```
232
+
233
+ Edit `parts/bracket/bracket.py` (or let your agent do it), then:
234
+
235
+ ```bash
236
+ b123d-print build bracket --formats 3mf,png
237
+ ```
238
+
239
+ ```bash
240
+ b123d-print check
241
+ ```
242
+
243
+ | Command | Does |
244
+ | --- | --- |
245
+ | `init [dir]` | create a workspace: `b123d-print.toml`, `AGENTS.md`, `parts/` |
246
+ | `new <name> [--from <example>]` | scaffold a part from the template or a bundled example |
247
+ | `build [parts] [--variant v] [--formats f]` | export + section views, report sizes |
248
+ | `check [parts] [-v] [--json]` | run the checks; exit code 1 on any failure |
249
+ | `examples` / `list` / `config` | bundled examples / parts in the workspace / settings in effect |
250
+
251
+ `AGENTS.md` (written by `init`) carries the workflow for agents that read it, such as Codex and
252
+ Cursor; for Claude Code put `@AGENTS.md` in `CLAUDE.md`. Running a part script directly also
253
+ works: `uv run parts/bracket/bracket.py`. The CLI does **not** sandbox: it runs your scripts
254
+ like any Python.
255
+
256
+ ## How a session goes
257
+
258
+ Start with one of the MCP prompts where your client supports them —
259
+ **`design_part_from_photos`**, **`modify_part`**, **`verify_part`**, **`calibrate_hole_comp`** —
260
+ or just ask; the server's instructions and the `b123d-print://guide` resource carry the same
261
+ rules. An illustrative, shortened exchange:
262
+
263
+ > **You:** A case for this PIR sensor, mounted on a wall outside. *(photos with calipers)*
264
+ >
265
+ > **Model:** Dimensions I read: body 69.6 × 24.5 × 19.1 (photo 2, calipers), dome Ø18.6
266
+ > (photo 3), dome center about 35.5 from the bottom (estimate). Nothing shows how far the dome
267
+ > sticks out. Please confirm or correct before I model.
268
+ >
269
+ > **You:** Dome sticks out 8. The rest is right.
270
+ >
271
+ > **Model:** *(new_part, write_part, build_part, render_preview, three sections, check; fixes a
272
+ > thin wall the check found, checks again)* Done: 35.1 × 38.8 × 76.6 mm, valid. The sensor gets
273
+ > 0.6 mm per side, the dovetail 0.2 per face. Prints on its floor without supports (one flat
274
+ > bridge over the groove). Exported 3MF and PNG.
275
+
276
+ | Tool | What it does |
277
+ | --- | --- |
278
+ | `list_parts` | parts, bundled examples, settings in effect, sandbox in use |
279
+ | `new_part(name, description?, example?)` | scaffold from the template, or copy a bundled example |
280
+ | `read_part` / `write_part` | read / replace the script (syntax-checked before saving) |
281
+ | `build_part(name, variant?)` | run it: bounding box, volume, validity, dimensions, errors |
282
+ | `render_preview(name, variant?, parts?)` | 6-view PNG; `parts="assembly"` shows the fit |
283
+ | `section(name, plane, at, ...)` | colored cross-section PNG on `XY`, `XZ` or `YZ` |
284
+ | `check(name, variant?)` | validity, overlap, overhang, wall and probe checks |
285
+ | `export(name, variant?, formats?)` | files to `parts/<name>/out/` |
286
+
287
+ ## Output formats
288
+
289
+ | File | For |
290
+ | --- | --- |
291
+ | `<name>.py` (always) | the parametric source: change a `Dims` value, rebuild everything |
292
+ | `.step` | editing in other CAD (Fusion, Onshape, FreeCAD, SolidWorks): an exact solid you can push/pull, fillet and cut, without feature history; changes there don't flow back to the script |
293
+ | `.stl` | any slicer |
294
+ | `.3mf` | slicers that read it (Bambu Studio, OrcaSlicer, PrusaSlicer); keeps units |
295
+ | `.png` | 6-view preview, assembly render, section views |
296
+
297
+ Pick them in your config (`formats = ["3mf", "png"]`), per run (`--formats step,3mf`), or with
298
+ `formats=` on the MCP `export` tool. Default: all four.
299
+
300
+ ## A part script
301
+
302
+ ```python
303
+ from dataclasses import dataclass
304
+ from build123d import Align, Box, Cylinder, Mode, Part
305
+ from b123d_print import View, Wall, hole_comp
306
+
307
+ BED = (Align.CENTER, Align.CENTER, Align.MIN)
308
+
309
+ @dataclass(frozen=True)
310
+ class Dims: # every dimension named, in mm, with its source
311
+ W: float = 30.0 # typed measurement
312
+ H: float = 8.0
313
+ HOLE_D: float = 3.4 # M3 clearance, target printed size
314
+ CBORE_D: float = 6.5
315
+ CBORE_DEPTH: float = 3.0
316
+ HOLE_COMP: float = hole_comp(__file__) # from YOUR config, default 0
317
+
318
+ VARIANTS = {"": {}, "wide": dict(W=40.0)} # overrides only
319
+
320
+ def build(d: Dims) -> Part: # print orientation, bed at Z=0
321
+ p = Box(d.W, d.W, d.H, align=BED)
322
+ p -= Cylinder((d.HOLE_D + d.HOLE_COMP) / 2, 50, mode=Mode.PRIVATE)
323
+ p -= Cylinder((d.CBORE_D + d.HOLE_COMP) / 2, d.CBORE_DEPTH, align=BED, mode=Mode.PRIVATE)
324
+ return p
325
+
326
+ def views(d): # sections saved with every export
327
+ return [View("XZ", 0.0, "through the hole")]
328
+
329
+ def checks(d): # how much material is left around the counterbore?
330
+ return [Wall("", "XY", d.CBORE_DEPTH / 2, near=(0, 0), radius=d.W, label="around the counterbore")]
331
+
332
+ if __name__ == "__main__":
333
+ from b123d_print import run
334
+ run(__file__)
335
+ ```
336
+
337
+ Optional `assembly(d)` returns the parts in assembled position plus stand-ins for what they
338
+ mate with (a PCB, a sensor); the overlap check makes sure nothing printed hits them. See
339
+ [`esp_ir_blaster`](src/b123d_print/examples/esp_ir_blaster/esp_ir_blaster.py) and
340
+ [`tuya_motion_sensor_outdoor_case`](src/b123d_print/examples/tuya_motion_sensor_outdoor_case/tuya_motion_sensor_outdoor_case.py).
341
+ The modeling API itself is documented in the [build123d docs](https://build123d.readthedocs.io).
342
+
343
+ ## Settings and hole compensation
344
+
345
+ FDM printers print small holes undersize, by an amount that depends on printer, nozzle,
346
+ material and slicer. b123d-print never assumes a value: `hole_comp` defaults to **0**.
347
+
348
+ ![Hole calibration coupon](docs/images/hole_coupon.png)
349
+
350
+ Print the coupon (`new_part("coupon", example="hole_coupon")` over MCP, or
351
+ `b123d-print new coupon --from hole_coupon`), measure each hole, and set the average error:
352
+
353
+ ```toml
354
+ # ~/.config/b123d-print/config.toml (`b123d-print config` shows what is in effect)
355
+ [print]
356
+ hole_comp = 0.4 # mm added to hole diameters when cutting
357
+ clearance = 0.2 # default mating gap
358
+ min_wall = 0.8 # Wall checks fail below this unless they set their own limit
359
+ max_overhang = 45 # degrees from vertical
360
+ formats = ["step", "stl", "3mf", "png"]
361
+ ```
362
+
363
+ A workspace `b123d-print.toml` overrides the user config; environment variables
364
+ (`B123D_PRINT_HOLE_COMP`, `B123D_PRINT_FORMATS`, ...) override both.
365
+
366
+ ## Printer profile (optional)
367
+
368
+ By default b123d-print is in **generic mode**: no printer assumed, 0.8 mm minimum wall, 45°
369
+ overhang limit. If you set your printer, the model also knows its build volume and nozzle:
370
+
371
+ ```bash
372
+ b123d-print printer list bambu a1 # search 405 printer models
373
+ ```
374
+
375
+ ```bash
376
+ b123d-print printer use bambu_lab_a1_mini --material petg
377
+ ```
378
+
379
+ - Parts must then fit the build volume in print orientation (`fit` check; it fails otherwise).
380
+ - Minimum wall becomes 2 line widths of your nozzle; material notes (PLA vs PETG vs ASA …) reach
381
+ the model.
382
+ - `b123d-print printer hole-comp 0.4` stores your coupon result for that printer + material.
383
+ - `b123d-print printer use generic` goes back. Over MCP: the `use_printer` tool.
384
+
385
+ Printer dimensions come from [OrcaSlicer](https://github.com/SoftFever/OrcaSlicer)'s printer
386
+ library (facts only; see [THIRD_PARTY.md](THIRD_PARTY.md)). Not listed, or modified? Add your own
387
+ `~/.config/b123d-print/printers/<name>.toml`:
388
+
389
+ ```toml
390
+ label = "My Ender 3 with 0.6 nozzle"
391
+ bed_x = 220
392
+ bed_y = 220
393
+ height = 250
394
+ nozzle = 0.6
395
+ # round_bed = true # deltas: bed_x is the diameter
396
+ # max_overhang = 50
397
+ ```
398
+
399
+ Materials work the same way in `materials/` (`label`, `notes`, optional `clearance`).
400
+
401
+ ## The checks
402
+
403
+ - **overlap** — intersection volume between every pair of `assembly(d)` parts; > 0.01 mm³ fails.
404
+ - **overhang** — downward faces in print orientation, angle from vertical (wall 0°, ceiling
405
+ 90°); > 45° is reported, the bed face is ignored, flat ceilings are marked `bridge`.
406
+ `Overhang(part, fail=True)` for parts that must print without supports.
407
+ - **wall** — thinnest material in a cross-section, optionally only around one cut: across the
408
+ wall along the surface normal, and between separate outlines (hole to edge). Tips of ordinary
409
+ corners don't count as thin; knife edges sharper than about 25° do.
410
+ - **fit** — only with a printer profile: the part fits the build volume in print orientation.
411
+ - **probe** — material must be present (or absent) in a small box.
412
+ - **valid** — every printable part is a valid, non-empty solid.
413
+
414
+ ## Sandbox
415
+
416
+ | Platform | Isolation |
417
+ | --- | --- |
418
+ | macOS | `sandbox-exec`: no network, no child processes, writes only to the part's `out/` and a scratch folder, home folder unreadable except the workspace |
419
+ | Linux with [bubblewrap](https://github.com/containers/bubblewrap) | no network, read-only system, same writable folders, home hidden |
420
+ | Linux without bubblewrap, Windows | separate process + audit hook only — **install bubblewrap, or use Docker** |
421
+ | Docker | the container (`--network none`, only the mounted folder) |
422
+
423
+ Every build also gets a timeout (default 120 s, `--timeout`) and a CPU limit. The audit hook
424
+ (blocks sockets, subprocesses, native libraries, writes elsewhere) is defense in depth, not a
425
+ boundary on its own.
426
+
427
+ ## Updating
428
+
429
+ `b123d-print --version` (or the `version` field of the MCP `list_parts` tool) shows what you run.
430
+ Releases and what changed: [GitHub releases](https://github.com/KD5RYN/b123d-print/releases).
431
+
432
+ | Installed with | Update |
433
+ | --- | --- |
434
+ | `uvx --from git+https://github.com/KD5RYN/b123d-print ...` (MCP clients) | uvx caches the build: run `uvx --refresh --from git+https://github.com/KD5RYN/b123d-print b123d-print-mcp --help` once, then restart the client |
435
+ | a pinned version `...b123d-print@v0.1.0` | change the tag in your client config, restart the client |
436
+ | `uv tool install git+https://github.com/KD5RYN/b123d-print` (CLI) | `uv tool install --force git+https://github.com/KD5RYN/b123d-print` |
437
+ | Docker | `git pull && docker build -t b123d-print-mcp .` in your clone, restart the client |
438
+ | Claude Code plugin | `/plugin marketplace update b123d-print`, then restart Claude Code |
439
+
440
+ After updating:
441
+
442
+ - **Workspaces** keep your parts and settings. The workflow text in a workspace's `AGENTS.md`
443
+ is a copy; refresh it with `b123d-print init --update-agents` (MCP clients always get the
444
+ current text from the server).
445
+ - **Printer profiles** ship with the package and update with it; your own files in
446
+ `~/.config/b123d-print/` are never touched.
447
+ - **Part scripts**: while b123d-print is 0.x the script conventions may change; the release
448
+ notes say when, and `b123d-print check` tells you what breaks.
449
+
450
+ ## Troubleshooting
451
+
452
+ | Symptom | Fix |
453
+ | --- | --- |
454
+ | First start is very slow, or the client gives up | it is downloading the dependencies; run `uvx --from git+https://github.com/KD5RYN/b123d-print b123d-print-mcp --help` once in a terminal, then restart the client |
455
+ | Client says `uvx` not found | use the full path from `which uvx` as the command |
456
+ | Model never looks at the renders | your client doesn't pass tool images to the model; open `out/*.png` yourself |
457
+ | `timed out after 120 s` | a slow or endless script; raise `--timeout` if the part is genuinely heavy |
458
+ | `blocked in part scripts: ...` | the script tried the network, a subprocess or files outside the workspace; part scripts only need build123d |
459
+ | Holes print small | calibrate `hole_comp` with the coupon |
460
+ | Linux: sandbox says "audit hook only" | install bubblewrap (`apt install bubblewrap`); on Ubuntu 24.04 unprivileged user namespaces must also be allowed — or use Docker |
461
+
462
+ ## Platforms and status
463
+
464
+ **0.x — early.** The part-script convention may still change. CI tests macOS and Ubuntu, the
465
+ built package via `uvx`, and the Docker image on every push. Windows is untested: the MCP
466
+ server should run there without the OS sandbox; Docker is the safe route.
467
+
468
+ ## Contributing
469
+
470
+ Issues and pull requests are welcome. To work on it:
471
+
472
+ ```bash
473
+ git clone https://github.com/KD5RYN/b123d-print && cd b123d-print && uv sync
474
+ ```
475
+
476
+ ```bash
477
+ uv run pytest && uv run b123d-print check && uv run python tests/mcp_smoke.py
478
+ ```
479
+
480
+ The printer table is generated: `python tools/sync_orca_printers.py` refreshes
481
+ `src/b123d_print/data/printers_orca.json` from OrcaSlicer (records the commit it used).
482
+
483
+ New checks and example parts should come from something that was actually printed.
484
+
485
+ ## Credits
486
+
487
+ b123d-print is a thin layer over:
488
+
489
+ - [build123d](https://github.com/gumyr/build123d) — the Python CAD API every part is written in (Apache-2.0)
490
+ - [Open CASCADE Technology](https://dev.opencascade.org), via [OCP](https://github.com/CadQuery/OCP) — the geometry kernel (LGPL-2.1 with exception; bindings Apache-2.0)
491
+ - [lib3mf](https://github.com/3MFConsortium/lib3mf) — 3MF export (BSD)
492
+ - [OrcaSlicer](https://github.com/SoftFever/OrcaSlicer) — printer dimensions for the optional printer profiles (AGPL-3.0; facts only, no files copied)
493
+ - [matplotlib](https://matplotlib.org) and [NumPy](https://numpy.org) — renders, sections, check math
494
+ - [Model Context Protocol](https://modelcontextprotocol.io) and its [Python SDK](https://github.com/modelcontextprotocol/python-sdk) (MIT)
495
+ - [uv](https://github.com/astral-sh/uv) — packaging and `uvx`
496
+
497
+ Details and redistribution notes: [THIRD_PARTY.md](THIRD_PARTY.md).
498
+
499
+ ## License
500
+
501
+ [MIT](LICENSE) © KD5RYN and contributors.