3d-modeling

saitotakeuchi's avatarfrom saitotakeuchi

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.

0stars🔀0forks📁View on GitHub🕐Updated Jan 11, 2026

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.
name3d-modeling
descriptionGenerate 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:

  1. Understand - Clarify dimensions, tolerances, and intended use if not specified
  2. Design - Create model using build123d operations
  3. Save - Write the Python script to a .py file
  4. Preview - Run mcc preview model <script.py> to visualize
  5. Validate - Run mcc validate mesh <script.py> to check printability
  6. 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:8080 showing live preview
  • Output folder: output/ - Place exported STL files here
  • tmux session: Running inside tmux (detach with Ctrl+B, D, reattach with tmux 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

  1. Read the current model.py to understand the starting template
  2. Modify model.py based on the user's request
  3. Wait - The browser automatically shows the updated model
  4. Verify - Check for Python errors before proceeding (see below)
  5. Ask the user for feedback and iterate
  6. 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.):

  1. Read the error message - it tells you exactly what's wrong
  2. Fix the issue in model.py
  3. Run python model.py again to verify the fix
  4. Only then ask the user for feedback

Common errors to watch for:

  • NameError: name 'Scale' is not defined → Use scale() function instead
  • SyntaxError → Check for missing colons, parentheses, quotes
  • TypeError → Check function arguments match expected types
  • AttributeError → 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() and sweep() 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:

  1. Create basic shape → save → check preview works
  2. Add one transformation → save → verify no errors
  3. Add boolean operations → save → check geometry
  4. 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:

  1. Check for non-manifold geometry (run mcc validate mesh --verbose)
  2. Verify boolean operations completed (no floating geometry)
  3. Use mcc repair auto for automatic fixes
  4. 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

  1. 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 artifacts
    
  2. Components 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 parts
    
  3. Multiple 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

  1. 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 part
    
  2. Ensure 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 solid
    
  3. Position 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 subtraction
    
  4. Verify 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:

  1. Check your boolean operations (+, -, &)
  2. Verify parts overlap where they should connect
  3. 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!