b123d-print-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| B123D_PRINT_WORKSPACE | No | The folder that holds your parts (parts/<name>/<name>.py); it is created if missing. Without --workspace the server uses $B123D_PRINT_WORKSPACE, else the folder the client starts it in. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| read_guideA | The workflow and print rules (the b123d-print://guide resource). Call it once before the first part in a session: many clients never load that resource. essentials=True: only the short checklist of the rules most often skipped. |
| list_partsB | List the part scripts in the workspace, with the settings in effect. |
| use_printerA | Optional printer profile (default is generic mode). search: find printer names ("bambu a1", "prusa mk4"). name: make it active (saved in the user's config, or over HTTP and SSH in this workspace's b123d-print.toml; "generic" clears it); a nickname given before works too. nickname: only for two printers of the same model, e.g. name='bambu_lab_p2s_0_4_nozzle', nickname='p2s-garage': each nickname keeps its own calibration. material: pla, petg, asa, abs, tpu or the user's own. Only change the printer when the user asks for it. |
| save_setupA | Save the user's first-run answers (the setup_workspace prompt): printer (a name from use_printer's search, or a nickname), material, formats ("3mf,png"), default style; hole_comp=0 records that the slicer corrects holes (the default way); a nonzero, MEASURED hole_comp / hole_comp_h is an opt-in that builds the correction into every file; calibration: other coupon results, e.g. {"clearance": 0.18, "max_bridge": 15}. With a printer, measured values are stored for it and the material. Written where use_printer writes. A value already set there is kept and listed as a conflict: ask the user, then call again with overwrite=True. Empty arguments change nothing. move_flat=True (after asking the user) moves measured values set for every printer of a workspace under the active printer. |
| new_partA | Create parts//.py from the template (Dims dataclass, build(d), VARIANTS, views, checks). name: lower_snake_case. example: copy a bundled example instead (see list_parts), e.g. 'hole_coupon' to calibrate hole compensation. sketch_first: the template with a sketches(d) outline that build(d) extrudes, for a part that is mostly a 2D shape made solid (offer it per part, see the guide's Sketch first); check the outline with render_sketch. Returns a short confirmation; the template is the b123d-print://template resource (or read_part), show_code=True returns the new file's code here. Usually the next step is write_part with the whole script. If you haven't, call read_guide first; confirm the dimensions with the user before writing the script. |
| read_partC | Return the current code of a part script. |
| write_partA | Replace the whole part script with |
| paramsA | The part's named dimensions, read from the script without running it: each Dims field with its type, default as written, value (when the default is a literal), the comment saying where it came from, and its line; the derived values (@property) and the fields they use; and each variant's overrides in VARIANTS. Change one with set_params, not a whole rewrite. |
| set_paramsA | Change Dims values in the script in place, leaving every other character of the file as it was: values {"FIELD": new value, ...} replaces each field's default (only literal defaults: a default computed by an expression such as hole_comp(file) is refused with the reason; so are unknown fields, derived @property values and values of the wrong type). variant: change (or add) that variant's override in VARIANTS instead of the default. rebuild (default true): build before and after (the build cache makes "before" quick) and report what changed per piece: size and volume; parts: only those pieces. Saved like write_part (size limit, must compile). If the new build fails, the result has the diagnosis and the call to undo it. |
| build_partA | Run the part script (sandboxed, with a timeout) and report, per variant and part: bounding box size and min/max (mm), volume (mm^3), solid count, is_valid, the Dims values, and any error with its traceback; and under "timing" each piece's face count and build time, with a hint for a piece that is slow or dense. variant: key from VARIANTS ('standard' = ''). parts: comma-separated piece names from build(): build only those (lazy pieces: the others are never built). Built pieces are cached in out/.cache/, so render_preview, section, check and export reuse them while the script, its Dims and the settings stay the same; fresh=True builds again and replaces the cached pieces. Writes no other files. Ends with "before_printing": the review steps this version still needs, each with its call. |
| diagnose_failureA | The deeper look after a failed or broken build (failed results already carry a short DIAGNOSIS): builds one variant piece by piece, each on its own and without the build cache, and for each piece reports the error with its likely cause and specific fixes (where in the script, the piece, the numbers: e.g. the largest fillet radius that works on those edges), or for a piece that built, what makes it unprintable (invalid solid, zero volume, a 2D shape, not on the bed) and, for an invalid one, which faces and edges are bad, tiny edges and sliver faces left by booleans, and whether part.fix() repairs it. Then the assembly. parts: only those pieces. Stops before the time limit and names the pieces it did not get to. |
| render_previewA | Render six views (iso, top, front, right, iso from below, bottom) as a PNG image. parts: comma-separated part names from build() (several are laid out side by side; only those are built), 'assembly' (assembled position with stand-ins), or omit: the assembly if the script has one, else every printable part side by side. One variant (default: the first). size: 'normal' or 'small' (~800 px wide, cheaper). inline=False returns only the file (over HTTP: its download link), not the image itself. Uses the pieces cached by an earlier build; fresh=True builds them again. Remote server (HTTP/SSH): the files stay on the server, as a temporary working copy deleted when unused; bring the part home with download_part (one zip in the parts// layout). Never use ssh, scp or a shell on the server: there is none. |
| sectionA | Cross-section as a PNG image, each part in its own color. plane: 'XY' (at = Z height), 'XZ' (at = Y) or 'YZ' (at = X), in mm. parts: default the assembly if the script has one (shows fit and clearances), else all printable parts; or comma-separated names (only those are built). size: 'normal' or 'small' (~800 px wide). inline=False returns only the file (over HTTP: its download link), not the image itself. Uses the pieces cached by an earlier build; fresh=True builds them again. Remote server (HTTP/SSH): the files stay on the server, as a temporary working copy deleted when unused; bring the part home with download_part (one zip in the parts// layout). Never use ssh, scp or a shell on the server: there is none. |
| render_sketchA | Sketch first: one 2D shape from the script's sketches(d) hook, drawn flat on a mm grid as a PNG, with automatic dimensions (overall size, hole diameters, slot widths, inside corner radii, hole and slot centres from the lower-left corner) and the sketch checks: one closed region, the narrowest neck against min_wall, inside corners under R0.5 (BP-R1), holes under 1 mm, the outline against the bed. Show it to the user and get an OK before build(d) extrudes, revolves or sweeps it. sketch: its name in sketches(d) (default: the first). dims=False draws the shape and grid only. Nothing 3D is built. size: 'normal' or 'small'. Remote server (HTTP/SSH): the files stay on the server, as a temporary working copy deleted when unused; bring the part home with download_part (one zip in the parts// layout). Never use ssh, scp or a shell on the server: there is none. |
| checkA | Run the print checks: validity, overlap between assembly parts (incl. stand-ins), overhangs > 45 deg from vertical in print orientation (bed face ignored) and bridge spans, thin features (walls under one line width fail), round-topped horizontal holes, fillets on bed edges, gaps between parts printed in place, the script's Wall checks (thinnest material around deep cuts) and Probes. The fit between an assembly's pieces (hole alignment, gaps) is a separate tool, check_fit, unless the script lists Mates() in checks(d). Fix every FAIL; report every WARN. On a remote server (HTTP/SSH) the part is not on the user's machine until download_part brings it home. Uses the pieces cached by an earlier build, and each unchanged piece's findings of the per-piece checks; fresh=True builds and checks everything again. A passing result ends with the review steps this version still needs, each with its call. |
| design_metricsA | Design numbers for the design review, per printable part: size and proportions, filament (simple estimate, roughly +-20 %) and cost, print time (rough estimate), supports, shallow slopes that show layer steps, edge treatment (sharp / filleted / chamfered shares, distinct radii and chamfer sizes), mirror symmetry, screw sizes, seam corners, water traps, inside corners, hole edge distance, slenderness across the layers, vase-mode layers and lean (and the thinnest walls for a style that asks); then the suggestions the part's style triggers, each citing its DS-* rule. ALWAYS before export, after check passes; report its suggestions to the user. style: override the part's STYLE (functional, modern, least_cost, rugged, soft, structural, organic, decorative, toy, or a style file of the user's: list_parts shows them). up: the direction that points up in use for the water-trap check (+X, -Y, ...; default the script's UP, else +Z). as_json: the full numbers as JSON instead of the text report. Changes nothing. |
| suggest_orientationA | Rank the ways to put the part on the bed, so it does not print upside down or on a thin edge: the six axis-aligned orientations and the large flat faces of its convex hull, each scored on supports (area and grams under overhangs past max_overhang, bridges apart), bed contact, height and print time (rough estimates), bridges, tip-over risk (as the check) and, with a load or the structural style, whether the load runs along the layers (DS-T1). Returns a ranked table with the Rot(x, y, z) to apply in build(d) and how. ALWAYS before export (a part already flat on the bed: it says so in a second). piece: one printable part (default: each). load: the bending or tension direction as build(d) returns the part ('X', 'along Y', 'XZ', '1,0,1'). image: also render the top choice (of the first part). Changes nothing. Remote server (HTTP/SSH): the files stay on the server, as a temporary working copy deleted when unused; bring the part home with download_part (one zip in the parts// layout). Never use ssh, scp or a shell on the server: there is none. |
| exportA | Export printable parts to parts//out/, sorted: print/ (3MF, STL, print sheet: for the
slicer), cad/ (STEP, GLB), images/, docs/ (BOM, view HTML), README.md. Default: every part, all variants. The
files must reach the user: on a remote server download_part brings them home.
parts: comma-separated part names from build() to build and export only those; variant: one
variant. Uses the pieces cached by an earlier build and leaves files already up to date as
they are (templates too); fresh=True builds and writes everything again.
formats: comma-separated from step (other CAD: Onshape, Fusion, FreeCAD), stl and 3mf
(slicers; 3MF keeps units), png (6-view preview + section views), glb (binary glTF, for web
and 3D apps), print (the print sheet, [_]print.md: size, filament and time
estimates, supports, tip-over, walls and infill, fits and hole compensation, PRINT_NOTES, for
the slicer, including declared hardware), hardware (versioned whole-assembly BOM JSON,
[]hardware.json), bom and bom_csv (the shopping list of that hardware as a
Markdown table, []_bom.md, and as CSV, bom.csv; hardware_list returns it in
the result), spec (the replication spec from MEASURE.md, []_spec.md, for a
part copied from a physical original; replication_spec returns it), or 'all'. Omit to use the user's |
| list_facesA | The flat faces of a printable part, for picking one for a face fit-test template (make_template kind='face'): stable ids (1 = the largest; the same geometry always gets the same ids, changed geometry may renumber), outward normal and side (bottom, top, front, back, left, right), area, center, bounding box, round holes (diameter, center) and other openings; curved faces are only counted. part: which printable part (default: the only one). highlight: a face id or side; also returns a picture of the part with that face in red, to confirm the choice with the user before making the template. size: 'normal' or 'small'. Remote server (HTTP/SSH): the files stay on the server, as a temporary working copy deleted when unused; bring the part home with download_part (one zip in the parts// layout). Never use ssh, scp or a shell on the server: there is none. |
| list_edgesA | The edges of a printable part, to pick the ones for a fillet or chamfer by id instead of
guessing a selector: stable ids (1 = the longest; changed geometry may renumber), type (line,
circle, arc, ellipse, spline), length, start and end (or center and radius), convexity (convex:
an outside edge; concave: an inside corner; tangent: a smooth joint such as a fillet's side;
seam), the turn between its faces and the two faces it joins (flat faces by their list_faces
id, curved ones by type). Filters: face (an id, side or 'x,y,z' as in list_faces: its edges),
kind, convexity, near ('x,y,z': edges within |
| measureB | Precise measurements on the built part, in mm, with the points where they were taken: check fits with numbers instead of guessing from pictures. kind: 'distance' (a, b: piece names from build() or assembly labels, stand-ins included): the smallest gap and its two points, or when they overlap the interference volume, its box and depth. Two assembly labels are measured where assembly(d) puts them (the fit). 'wall' (at: x,y,z on or in the material; direction: optional x,y,z): the thickness along a line through the point (default: into the part along the nearest face's normal), with the gaps and walls beyond it. 'faces' (face, face2: ids or sides from list_faces): the distance between two parallel flat faces (a wall, a slot), or the angle between them, and their closest points. 'hole' (face: a flat face id or side, and/or at: a point at the hole): each round hole's modeled diameter, what it prints at after hole_comp, center, axis, through or blind depth. a: the piece for wall, faces and hole (default: the only one). frame: 'auto' (default), 'print' (as build() returns it) or 'assembly' (as assembly(d) places it); points are in that frame. image: also a picture of the part with what was measured in red. Uses the build cache. |
| make_templateA | A fit-test template: a quick thin print cut from the real part (same hole compensation and
clearances) to check size, hole pattern and mating fits before printing the whole part. kind:
'slice' (plane XY/XZ/YZ at = Z/Y/X: the part between two planes |
| download_partA | Bring a whole part home from a remote server, in the same layout a local install writes:
parts//.py, parts//out/ (exports, renders, sections) and parts//ref/
(reference models). Over HTTP: ONE signed link (no token needed, expires) to a zip of all of
it; run |
| compareA | Compare a part with a reference model: bounding box, volume and surface area differences, surface distances (mean, Hausdorff) after aligning orientation, an F-score, a 0-100 score and pass/fail. reference, candidate: STL, 3MF or STEP files inside the workspace, as paths relative to it (e.g. 'refs/bracket.stl', 'parts/bracket/out/print/bracket.3mf'). thresholds: optional overrides, comma-separated name=value (e.g. 'hausdorff_mm=2,volume_rel=0.05'). image: also return a view of the reference colored by deviation (green = matches, red = off); size: 'normal' or 'small' (~800 px wide); inline=False returns only its file (over HTTP: a link). Remote server (HTTP/SSH): the files stay on the server, as a temporary working copy deleted when unused; bring the part home with download_part (one zip in the parts// layout). Never use ssh, scp or a shell on the server: there is none. |
| save_versionA | Save a version of the project in its version history (git, on the user's machine): each part's script, its ref/ folder, MEASURE.md and the .py files beside it, and b123d-print.toml; never out/. Only when the user said yes: after a change that built and passed its checks, OFFER "Save this version? (: )", never save on your own. message: what changed, in the user's words ("arm 115 -> 130 mm"); part: only that part (default: all). The result says what was saved and the changed Dims values. start=True first makes the project folder a git repository with a .gitignore: only after the user agreed to keep a version history. On a shared (HTTP/SSH) server it runs nothing and returns the commands for the user's own project folder. |
| list_versionsB | The saved versions, newest first: date, id, message and the Dims values that changed. With part: numbered versions of that part (for restore_version), and what is not saved yet. On a shared (HTTP/SSH) server: the command for the user's own project folder. |
| restore_versionA | Bring a part (script, ref/, MEASURE.md) back to a saved version: a number from list_versions(part), or a commit id. It is saved as a NEW version, so nothing is lost: no reset, no deleted history, files added later are kept. Refused while the part has changes not saved yet (offer save_version first). Ask the user before restoring; then build_part and check. On a shared (HTTP/SSH) server: the commands for the user's project folder (version = the commit id), then write_part to put the restored script back on the server. |
| upload_referenceA | Get an original model (STL, 3MF or STEP) onto the server, into parts//ref/,
to remix it, or a scan or photo (PNG, JPEG, TIFF, BMP) to trace with trace_outline (the part
folder may not exist yet). With data_base64 (the whole file, base64) it
is stored now, up to 50 MB; that is only practical for small files, since the model has to
write out every byte. Without it, the result says how the USER copies the file: over HTTP a
signed upload link for |
| describe_modelA | Inspect an original model before remixing it: format, units (and whether they look wrong), size, volume, triangles and faces before/after merging coplanar triangles, how much is curved (faceted), round holes (diameter, axis, center), repairs needed, the designer/license metadata the file carries, and a suggestion: MODIFY it or REDRAW it. part: the part it is for. file: a name in parts//ref/ (or a workspace path such as 'refs/x.stl'). scale: '' (as the file says), a factor, or the unit the file was drawn in ('inch', 'cm'). image: also a 6-view render (saved as parts//out/images/_original.png). |
| before_afterB | For a remix: the original and the remix side by side, and the original colored by how far the remix is from it (compare's deviation view: green = unchanged, red = changed), with compare's score and metrics. The original is what build() loads with import_model, or what the script's original(d) returns. export writes the same two images when it writes PNGs. |
| check_fitA | How the pieces of assembly(d) fit together, as placed (stand-ins count as obstacles): interference (pairs that overlap: the volume and where; pairs closer than the clearance without touching: the gap and where; faces resting on each other are contact) and hole alignment (round holes of two pieces on one axis are partners: offset beyond hole_tol mm, default 0.2, fails; their diameters read as screw holes, clearance + pilot / insert / nut, and a pair no single screw explains warns; a hole whose axis runs into solid material of the next piece has no partner). Male/female joint pairs from the script's joints(d) (or a Mates(joints=...) in checks(d)) are intentional fits measured against their own clearance. Stand-ins: labels saying stand-in or dummy, or matching no piece of build(). verbose: list the passing pairs too. One variant, or all. Uses the build cache (fresh=True rebuilds). |
| hardware_listA | What to buy: the bought hardware the script declares in hardware(d) (screws, inserts,
magnets, bearings), as one Markdown table to show the user: Qty | Item | Specification |
Used for | Source/link | Status ("check before ordering" where the specification is not
confirmed: ask the user, never guess it). Identical items are merged and summed. Offer it
when a part declares hardware. name: one part; parts: comma-separated part names to merge
into one list (each may be part:variant); neither: every part in the workspace. variant: the
variant (default: the standard one). count: how many assemblies ("for 4 brackets"), which
multiplies every quantity. format: 'table' (Markdown) or 'csv' (for a spreadsheet or cart).
Reads only hardware(d): builds nothing. The |
| replication_specA | The replication spec of a part copied from a physical original (the replicate_part
prompt): its MEASURE.md table (Feature | Value | Method | Accuracy | Status | Class | Source |
Field | Why changed) joined with the script's Dims values, as one Markdown table (Feature |
Field | Class | Source | Measured | Tol | Modeled | Δ | Why changed) plus flags: a critical
value still an estimate or not measured, readings spread wider than their accuracy (draft, an
oval hole, warp), a critical value modeled differently with no reason, a Field the script
lacks. Several readings in a Value cell ("4.21, 4.19, 4.22") are averaged. variant: whose Dims
(default: the standard one). Reads MEASURE.md and the Dims only: builds nothing, changes
nothing. The |
| trace_outlineA | Trace a flat object in a scan or photo (parts//ref/, from upload_reference) into a
2D outline in mm for a part script: the largest object's outline and its holes, simplified to
|
| recipeA | Tested build123d recipes for common printable features, with printing notes and sources: teardrop holes, counterbores and countersinks, nut traps, magnet pockets, snap arms (with strain sizing), living hinges, print-in-place hinges, lids with a lip, text, bed-edge chamfers, zip-tie and cable slots, ribs and gussets. name: '' lists them; a recipe (or one of its function names) gives its code, an example and the notes. Use one before improvising the feature; part scripts import them: from b123d_print.recipes import snap_arm. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| design_part_from_photos | Design a new part from photos and measurements: confirm dimensions first, then model, verify and report. |
| modify_part | Change an existing part, keeping its conventions, and re-verify. |
| remix_part | Remix someone else's model (STL, 3MF or STEP): inspect it, choose MODIFY or REDRAW, confirm the changes, then build, check (inherited vs new findings) and export. |
| replicate_part | Copy a physical part (often injection-molded) as a printable one: a measurement plan, each dimension critical or free, molding features changed for FDM, a replication spec in MEASURE.md checked against the Dims, and a check of the copy against the original. |
| verify_part | Review an existing part against the rules and report findings. |
| design_review | Review a part that passes the print checks for its purpose and design style: metrics, renders, 3-7 concrete suggestions with rule IDs. Changes nothing without the user's OK. |
| fit_test | Make a fit-test template of a part: a quick thin print of a face, slice or region, to check size, hole pattern and mating fits before the full print. |
| setup_workspace | First-run setup: ask the user for the printer, material, export formats and default style, save them with save_setup, then offer the calibration coupons in order. |
| calibrate_hole_comp | Walk through measuring HOLE_COMP with the calibration coupon. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| guide | The workflow and print rules (same text as AGENTS.md). |
| template | The part-script template new_part starts from. |
| recipes_index | The recipe library: tested build123d functions for common printable features. |
TDQS
Scored across 35 tools
Most tools have clearly distinct jobs across the design-to-print lifecycle, and descriptions explicitly separate close pairs such as check vs check_fit and build_part vs diagnose_failure. A few boundaries remain fuzzy, notably save_setup vs use_printer for configuration, compare vs before_after for reference comparison, and measure vs check_fit for fit numbers.
The dominant pattern is snake_case verb_noun, as in read_part, build_part, check_fit, save_version, and upload_reference. Some names break the pattern with single verbs/nouns such as section, export, recipe, params, check, and measure, and hardware_list reverses the verb_noun order, but the convention remains mostly readable and predictable.
At 35 tools, the surface is well above the rubric's well-scoped range and feels heavy for an agent to manage. The broad CAD/printing workflow justifies many specialized operations, but several fine-grained tools could likely be consolidated under action parameters.
The set covers an unusually complete workflow: onboarding and recipes, part authoring/editing, build and diagnosis, print checks and fit measurement, rendering/export, version history, remote transfer, reference inspection/tracing, and hardware BOMs. Minor gaps remain, such as no explicit delete/rename part operation and some workspace/file administration left implicit.