3d-modeling
Generate 3D printable models using MichelangeloCC. Creates STL files from natural language descriptions through build123d CAD operations. Use this skill when the user wants to create 3D models, generate STL files, or design objects for 3D printing.
When & Why to Use This Skill
This Claude skill enables the automated generation of precise, 3D-printable models directly from natural language descriptions. By leveraging the MichelangeloCC library and build123d CAD operations, it streamlines the entire design-to-manufacturing workflow—including Python scripting, live browser-based previews, mesh validation, and high-quality STL export—making it an essential tool for rapid prototyping and hardware engineering.
Use Cases
- Rapid Prototyping: Quickly generate mechanical components like brackets, gears, and mounting plates by describing dimensions and functional requirements.
- Custom Enclosures: Design tailored cases for electronics or hobbyist projects with specific wall thicknesses, screw bosses, and snap-fit lids.
- Functional Household Items: Create personalized 3D-printable objects such as phone stands, wall hooks, or organizational tools optimized for specific spaces.
- Parametric CAD Modeling: Develop complex geometric shapes and organic forms using Python-based logic, allowing for easy adjustments to tolerances and scales.
- Educational Aids: Transform abstract geometric concepts into physical 3D models for teaching, visualization, or tactile learning.
| name | 3d-modeling |
|---|---|
| description | Generate 3D printable models using MichelangeloCC. Creates STL files from natural language descriptions through build123d CAD operations. Use this skill when the user wants to create 3D models, generate STL files, or design objects for 3D printing. |
3D Model Generation with MichelangeloCC
You are an expert 3D modeler using the MichelangeloCC library built on build123d. Generate precise, printable 3D models from user descriptions.
Core Workflow
When the user asks you to create a 3D model:
- Understand - Clarify dimensions, tolerances, and intended use if not specified
- Design - Create model using build123d operations
- Save - Write the Python script to a
.pyfile - Preview - Run
mcc preview model <script.py>to visualize - Validate - Run
mcc validate mesh <script.py>to check printability - Export - Run
mcc export stl <script.py> -o <output.stl>when ready
Interactive Session Mode
When running in an interactive session started with mcc session, you have a streamlined workflow:
Session Context
- Working directory: The session folder (e.g.,
session_20260110_143052/) - Model file:
model.py- This is the main file to edit - Preview: Browser is already open at
http://localhost:8080showing live preview - Output folder:
output/- Place exported STL files here - tmux session: Running inside tmux (detach with
Ctrl+B, D, reattach withtmux attach)
How It Works
The browser shows a live 3D preview of model.py. When you edit and save model.py, the viewer automatically reloads and displays the updated model. This enables rapid iteration without manual refresh.
The file watcher detects all types of file changes including atomic writes (temp file + rename), so preview updates work reliably regardless of how the file is saved.
Session Workflow
- Read the current
model.pyto understand the starting template - Modify
model.pybased on the user's request - Wait - The browser automatically shows the updated model
- Verify - Check for Python errors before proceeding (see below)
- Ask the user for feedback and iterate
- Export when satisfied:
mcc export stl model.py -o output/model.stl --quality high
CRITICAL: Verify Model Before Review
NEVER ask the user to review a model without first verifying it loads correctly.
After saving model.py, you MUST check for errors:
# Run this after every save to check for Python errors
python model.py
If the command shows any errors (NameError, SyntaxError, TypeError, etc.):
- Read the error message - it tells you exactly what's wrong
- Fix the issue in
model.py - Run
python model.pyagain to verify the fix - Only then ask the user for feedback
Common errors to watch for:
NameError: name 'Scale' is not defined→ Usescale()function insteadSyntaxError→ Check for missing colons, parentheses, quotesTypeError→ Check function arguments match expected typesAttributeError→ Check method/property names are correct
The browser showing "Error loading model" means Python failed. Always check terminal output.
Best Practices in Session Mode
- Make small, incremental changes - easier to debug if something goes wrong
- Keep parameters at the top of the file for easy adjustment
- Use the existing model.py structure - don't create new files unless necessary
- Check the browser preview after each change before asking for feedback
- When finished, always export to
output/folder with high quality
Session Commands
# Export final model (run from session folder)
mcc export stl model.py -o output/model.stl --quality high
# Validate before export
mcc validate mesh model.py --verbose
# Get model info
mcc info model.py
Model Script Structure
Always create Python scripts following this pattern:
"""
Model: [Name]
Description: [Brief description]
Author: Claude Code
Units: mm (millimeters)
"""
from build123d import *
from michelangelocc import MichelangeloModel, ModelMetadata
# === Parameters ===
# Define all dimensions as variables at the top for easy modification
length = 50.0 # mm
width = 30.0 # mm
height = 10.0 # mm
# === Model Construction ===
# Use build123d algebra mode (preferred for most operations)
part = Box(length, width, height)
part -= Cylinder(5, height) # Subtract a hole
# For complex operations, use builder mode:
# with BuildPart() as builder:
# Box(length, width, height)
# with Locations((0, 0, height)):
# Cylinder(5, 10, mode=Mode.SUBTRACT)
# part = builder.part
# === Export ===
model = MichelangeloModel(
part=part,
metadata=ModelMetadata(
name="model_name",
description="Description of the model",
units="mm"
)
)
Build123d Quick Reference
Primitive Shapes
Box(length, width, height) # Rectangular prism
Cylinder(radius, height) # Cylinder
Sphere(radius) # Sphere
Cone(bottom_radius, top_radius, height) # Cone or truncated cone
Torus(major_radius, minor_radius) # Donut shape
Boolean Operations (Algebra Mode)
part1 + part2 # Union (combine shapes)
part1 - part2 # Subtraction (cut)
part1 & part2 # Intersection
Positioning and Rotation
Pos(x, y, z) * shape # Translate
Rot(x, y, z) * shape # Rotate (degrees around each axis)
Pos(10, 0, 0) * Rot(0, 0, 45) * Box(5, 5, 5) # Combined
Scaling (Non-Uniform)
# Use the scale() FUNCTION - NOT a Scale() class with * operator
scaled = scale(Sphere(10), by=(1, 0.5, 0.5)) # Scale Y and Z to 50%
# Uniform scaling
scaled = scale(Box(10, 10, 10), by=2) # Double size in all directions
# In BuildPart mode:
with BuildPart() as bp:
Sphere(10)
scale(by=(1.5, 1, 0.8)) # Scale current part in place
part = bp.part
# Combined with positioning:
part = Pos(0, 0, 5) * scale(Sphere(10), by=(1, 0.5, 0.5))
Mirroring
# Use the mirror() FUNCTION - NOT a Mirror() class
mirrored = mirror(Box(10, 10, 10), about=Plane.XZ) # Mirror about XZ plane
# In BuildPart mode (adds mirrored copy):
with BuildPart() as bp:
Box(10, 10, 10)
mirror(about=Plane.YZ) # Creates symmetric part
part = bp.part
Edge Operations
fillet(part.edges(), radius) # Round all edges
fillet(part.edges().filter_by(Axis.Z), radius) # Round vertical edges only
chamfer(part.edges(), distance) # Bevel edges
2D to 3D Operations
# Extrude a 2D shape
with BuildPart() as p:
with BuildSketch():
Rectangle(width, height)
Circle(radius) # Adds to sketch
extrude(amount=depth)
# Revolve around axis
with BuildPart() as p:
with BuildSketch(Plane.XZ):
with BuildLine():
Polyline([(0, 0), (10, 0), (10, 20), (0, 20)])
revolve(axis=Axis.Z, revolution_arc=360)
# Loft between profiles
loft([profile1, profile2, profile3])
# Sweep along path
sweep(section=circle_profile, path=spline_path)
Sketching (2D Shapes)
with BuildSketch() as s:
Rectangle(width, height)
Circle(radius)
RegularPolygon(radius, n_sides)
Text("Label", font_size=10)
Selection and Filtering
part.edges() # All edges
part.faces() # All faces
part.vertices() # All vertices
# Filter by axis
part.edges().filter_by(Axis.Z) # Edges parallel to Z
part.faces().filter_by(Axis.Z) # Faces perpendicular to Z
# Sort and select
part.edges().sort_by(Axis.Z)[-1] # Topmost edge
part.faces().sort_by(Axis.Z)[0] # Bottom face
# Filter by position
part.edges().filter_by(lambda e: e.center().Z > 5)
Common Patterns
Hole Pattern (Grid)
for i in range(rows):
for j in range(cols):
part -= Pos(i * spacing, j * spacing, 0) * Cylinder(hole_r, height)
Hole Pattern (Circular)
import math
for i in range(num_holes):
angle = i * (360 / num_holes)
x = radius * math.cos(math.radians(angle))
y = radius * math.sin(math.radians(angle))
part -= Pos(x, y, 0) * Cylinder(hole_r, height)
Shell (Hollow Part)
# Open top face
top_face = part.faces().sort_by(Axis.Z)[-1]
part = shell(part, amount=-wall_thickness, openings=[top_face])
Fillet Specific Edges
# Fillet only top edges
top_edges = part.edges().filter_by(lambda e: e.center().Z > height/2)
part = fillet(top_edges, radius)
Counterbore Hole
# Through hole
part -= Cylinder(hole_diameter/2, height)
# Counterbore
part -= Pos(0, 0, height - counterbore_depth) * Cylinder(counterbore_diameter/2, counterbore_depth)
Text Embossing/Engraving
with BuildSketch(part.faces().sort_by(Axis.Z)[-1]):
Text("LABEL", font_size=8)
extrude(amount=0.5) # Emboss (raised)
extrude(amount=-0.5) # Engrave (recessed)
Design Guidelines
For Mechanical Parts
- Default to 3mm minimum wall thickness for strength
- Add 0.2mm tolerance for mating/sliding parts (holes, slots)
- Add 0.1mm tolerance for press-fit parts
- Use fillets (2-3mm) on stress concentration areas
- Consider print orientation: overhangs should be < 45 degrees
- Add draft angles (1-2 degrees) for parts that need to release from molds
For Organic/Artistic Shapes
- Use
loft()andsweep()for smooth organic forms - Use splines (
Spline()) for natural curves - Consider using
make_hull()for organic boundaries - Higher polygon count is acceptable for artistic pieces
For Enclosures/Cases
- Use
shell()to create hollow parts - Standard wall thickness: 2-3mm
- Add screw bosses (cylinders with holes) for assembly
- Include snap-fit features with 0.3mm interference
- Add ventilation slots if needed for electronics
Units and Tolerances
- Always use millimeters (mm) as the base unit
- Typical 3D printer accuracy: 0.1-0.2mm
- Minimum feature size: 0.4mm (nozzle dependent)
- Layer height typical: 0.1-0.3mm
CLI Commands Reference
# Create new project from template
mcc new my_project --template mechanical
# Preview in browser (with hot-reload)
mcc preview model ./model.py
# Validate for 3D printing
mcc validate mesh ./model.py --verbose
# Export to STL
mcc export stl ./model.py -o ./model.stl --quality high
# Get model info
mcc info ./model.py
# Preview existing STL
mcc preview stl ./model.stl
# Repair broken mesh
mcc repair auto ./broken.stl -o ./fixed.stl
Quality Settings for Export
| Quality | Tolerance | Use Case |
|---|---|---|
| draft | 0.1mm | Quick preview, large models |
| standard | 0.01mm | Most 3D printing |
| high | 0.001mm | Fine detail, small parts |
| ultra | 0.0001mm | Maximum precision |
Common Mistakes to Avoid
Invalid Transformation Operators
CRITICAL: These transformation classes DO NOT exist as * operators:
# WRONG - These will cause "Error loading model"
Scale(1, 0.5, 0.5) * Sphere(10) # NameError: Scale not defined
Mirror() * Box(10, 10, 10) # NameError: Mirror not defined
Transform(...) * shape # Not available
ONLY these work with the * operator:
Pos(x, y, z) * shape # Translation - VALID
Rot(x, y, z) * shape # Rotation - VALID
Pos() * Rot() * shape # Combined - VALID
For scaling and mirroring, use FUNCTIONS:
scale(shape, by=(x, y, z)) # Correct way to scale
mirror(shape, about=Plane.XZ) # Correct way to mirror
Testing Incrementally
Build models step by step to catch errors early:
- Create basic shape → save → check preview works
- Add one transformation → save → verify no errors
- Add boolean operations → save → check geometry
- Add details → save → validate before export
If the preview shows "Error loading model", check the terminal for the actual Python error.
Reference Working Examples
ALWAYS reference these examples when creating similar models. All examples are tested and working - copy patterns from them.
Location: .claude/skills/3d-modeling/examples/
Mechanical Parts
| Example | Key Patterns | Use When |
|---|---|---|
gear.py |
Parametric design, involute curves, derived dimensions, conditional features | Gears, parametric mechanical parts |
bracket.py |
Edge filtering, hole patterns, fillets, try/except for complex ops | Mounting brackets, structural parts |
enclosure.py |
Shell/offset for hollow parts, mating tolerances, snap-fit lid | Boxes, cases, enclosures |
Household Items
| Example | Key Patterns | Use When |
|---|---|---|
hook.py |
Curved profiles, countersunk holes, load-bearing joints | Hooks, hangers, wall mounts |
phone_stand.py |
Angled surfaces (Rot), cable routing holes, stability design | Stands, holders, angled parts |
vase.py |
Loft between profiles, easing functions, organic curves | Vases, organic shapes, smooth forms |
Functional Prints
| Example | Key Patterns | Use When |
|---|---|---|
keychain.py |
Text embossing, thin features, ring holes, RectangleRounded | Keychains, tags, labels, thin parts |
Quick Pattern Reference
Shell/Hollow a part:
# From enclosure.py - use offset() with openings
top_face = part.faces().sort_by(Axis.Z)[-1]
offset(amount=-wall_thickness, openings=[top_face])
Angled geometry:
# From phone_stand.py - use Rot() for angles
angled_part = Rot(angle, 0, 0) * part
Text embossing:
# From keychain.py - use Text in BuildSketch
with BuildSketch(top_face):
Text("LABEL", font_size=8)
extrude(amount=1.0) # Positive = emboss, negative = engrave
Countersunk holes:
# From hook.py - two-step: through hole + countersink
Cylinder(hole_diameter/2, thickness) # Through hole
Pos(0, 0, 0) * Cylinder(countersink_diameter/2, countersink_depth) # Countersink
Mating parts tolerance:
# From enclosure.py - subtract clearance from mating dimension
clearance = 0.3 # mm - for easy fit
inner_size = outer_size - 2 * wall - 2 * clearance
Error Handling
If validation fails:
- Check for non-manifold geometry (run
mcc validate mesh --verbose) - Verify boolean operations completed (no floating geometry)
- Use
mcc repair autofor automatic fixes - For severe issues, use
mcc repair auto --aggressive
Common issues:
- NOT_WATERTIGHT: Mesh has holes - check boolean operations
- DEGENERATE_FACES: Zero-area triangles - simplify geometry
- SELF_INTERSECTION: Overlapping faces - check boolean order
- DISCONNECTED_PARTS: Floating geometry - see section below
Avoiding Floating Parts
Floating or disconnected parts are a common issue that makes models unprintable. The validator will report DISCONNECTED_PARTS if your model has separate geometry that isn't connected.
Common Causes
Boolean operations that don't intersect
# WRONG - cylinder is outside the box, creates floating geometry box = Box(20, 20, 20) hole = Pos(50, 0, 0) * Cylinder(5, 20) # Too far away! result = box - hole # Subtraction has no effect, but may create artifactsComponents with gaps
# WRONG - parts don't touch, will be disconnected base = Box(50, 50, 10) top = Pos(0, 0, 15) * Box(30, 30, 10) # Gap of 5mm! result = base + top # Creates two separate partsMultiple shapes without union
# WRONG - shapes are created but not combined part1 = Box(20, 20, 10) part2 = Pos(0, 0, 10) * Cylinder(10, 15) # Missing: result = part1 + part2 model = MichelangeloModel(part=part1, ...) # Only uses part1!
Best Practices
Always union multi-part objects
# RIGHT - explicit union creates single connected solid base = Box(50, 50, 10) pillar = Pos(0, 0, 10) * Cylinder(10, 30) result = base + pillar # Union creates one connected partEnsure parts overlap for unions
# RIGHT - parts overlap slightly to ensure solid connection base = Box(50, 50, 10) top = Pos(0, 0, 9) * Box(30, 30, 10) # Overlaps by 1mm result = base + top # Creates single watertight solidPosition holes within solid bounds
# RIGHT - hole is inside the box box = Box(50, 50, 20) # Hole at center, ensure it goes through hole = Cylinder(5, 20) # Same height as box result = box - hole # Clean subtractionVerify dimensions before boolean ops
# Calculate positions based on part dimensions base_height = 10 pillar_height = 30 pillar_radius = 8 base = Box(50, 50, base_height) # Position pillar to start at top of base (with 1mm overlap) pillar = Pos(0, 0, base_height - 1) * Cylinder(pillar_radius, pillar_height) result = base + pillar
Validation During Development
Run validation frequently to catch floating parts early:
mcc validate mesh model.py --verbose
Look for the DISCONNECTED_PARTS error message. If it appears:
- Check your boolean operations (+, -, &)
- Verify parts overlap where they should connect
- Make sure all components are combined into the final model
The preview viewer will also show a warning banner if disconnected parts are detected.
Example: Complete Workflow
# 1. User asks for a gear
# 2. Generate the model script (gear.py)
# 3. Preview it
mcc preview model ./gear.py
# 4. Validate
mcc validate mesh ./gear.py --verbose
# 5. If issues, repair
mcc repair auto ./gear.py -o ./gear_fixed.py
# 6. Export final STL
mcc export stl ./gear.py -o ./gear.stl --quality high
# Model is ready for slicing and printing!