Meshy-3d-agent meshy-3d-printing

3D print models generated with Meshy AI. Handles slicer detection, white model printing, multi-color printing via API, and print-optimized download workflows. Use when the user mentions 3D printing, slicing, Bambu, OrcaSlicer, Prusa, Cura, Creality Print, Elegoo, Anycubic, multicolor, multi-color, 3mf, or wants to print a figurine, miniature, or physical model.

install
source · Clone the upstream repo
git clone https://github.com/meshy-dev/meshy-3d-agent
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/meshy-dev/meshy-3d-agent "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/meshy-3d-printing" ~/.claude/skills/meshy-dev-meshy-3d-agent-meshy-3d-printing && rm -rf "$T"
manifest: skills/meshy-3d-printing/SKILL.md
source content

Meshy 3D Printing

Prepare and send Meshy-generated 3D models to a slicer for 3D printing. Supports white model (single-color) and multicolor printing workflows with automatic slicer detection.

Prerequisite: This skill reuses the utility functions (

create_task
,
poll_task
,
download
,
get_project_dir
, etc.) and environment setup from
meshy-3d-generation
. However, when the user wants to 3D print, this skill controls the entire workflow — including generation, format selection, downloading, and slicer integration. Do NOT run
meshy-3d-generation
's workflow first and then hand off here — this skill must control parameters from the start (e.g.
target_formats
with
"3mf"
for multicolor).


Intent Detection

Proactively suggest 3D printing when these keywords appear in the user's request:

  • Direct: print, 3d print, slicer, slice, bambu, orca, prusa, cura, multicolor, multi-color, 3mf
  • Implied: figurine, miniature, statue, physical model, desk toy, phone stand

When detected, guide the user through the appropriate print pipeline below.


Decision Tree: White Model vs Multicolor

IMPORTANT: When the user wants to 3D print, follow this flow:

  1. Detect installed slicers first (see Slicer Detection Script below)
  2. Ask the user: "Do you want a single-color (white) print or multicolor?"
  3. If white model → follow White Model Pipeline
  4. If multicolor: a. Check if a multicolor-capable slicer is installed b. Supported multicolor slicers: OrcaSlicer, Bambu Studio, Creality Print, Elegoo Slicer, Anycubic Slicer Next c. If no multicolor slicer detected, warn the user and suggest installing one d. Ask: "How many colors? (default: 4, max: 16)" and "Segmentation depth? (3=coarse, 6=fine, default: 4)" e. Confirm cost: generation (20) + texture (10) + multicolor (10) = 40 credits total f. Follow Multicolor Pipeline

Slicer Detection Script

Append this to the reusable script template from

meshy-3d-generation
:

import subprocess, shutil, platform, os, glob as glob_mod

SLICER_MAP = {
    "OrcaSlicer":           {"mac_app": "OrcaSlicer",          "win_exe": "orca-slicer.exe",         "win_dir": "OrcaSlicer",          "linux_exe": "orca-slicer"},
    "Bambu Studio":         {"mac_app": "BambuStudio",         "win_exe": "bambu-studio.exe",        "win_dir": "BambuStudio",         "linux_exe": "bambu-studio"},
    "Creality Print":       {"mac_app": "Creality Print",      "win_exe": "CrealityPrint.exe",       "win_dir": "Creality Print*",     "linux_exe": None},
    "Elegoo Slicer":        {"mac_app": "ElegooSlicer",        "win_exe": "elegoo-slicer.exe",       "win_dir": "ElegooSlicer",        "linux_exe": None},
    "Anycubic Slicer Next": {"mac_app": "AnycubicSlicerNext",  "win_exe": "AnycubicSlicerNext.exe",  "win_dir": "AnycubicSlicerNext",  "linux_exe": None},
    "PrusaSlicer":          {"mac_app": "PrusaSlicer",         "win_exe": "prusa-slicer.exe",        "win_dir": "PrusaSlicer",         "linux_exe": "prusa-slicer"},
    "UltiMaker Cura":       {"mac_app": "UltiMaker Cura",      "win_exe": "UltiMaker-Cura.exe",     "win_dir": "UltiMaker Cura*",     "linux_exe": None},
}
MULTICOLOR_SLICERS = {"OrcaSlicer", "Bambu Studio", "Creality Print", "Elegoo Slicer", "Anycubic Slicer Next"}

def detect_slicers():
    """Detect installed slicer software. Returns list of {name, path, multicolor}."""
    found = []
    system = platform.system()
    for name, info in SLICER_MAP.items():
        path = None
        if system == "Darwin":
            app = info.get("mac_app")
            if app and os.path.exists(f"/Applications/{app}.app"):
                path = f"/Applications/{app}.app"
        elif system == "Windows":
            win_dir = info.get("win_dir", "")
            win_exe = info.get("win_exe", "")
            for base in [os.environ.get("ProgramFiles", r"C:\Program Files"),
                         os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)")]:
                if "*" in win_dir:
                    matches = glob_mod.glob(os.path.join(base, win_dir, win_exe))
                    if matches:
                        path = matches[0]
                        break
                else:
                    candidate = os.path.join(base, win_dir, win_exe)
                    if os.path.exists(candidate):
                        path = candidate
                        break
        else:  # Linux
            exe = info.get("linux_exe")
            if exe:
                path = shutil.which(exe)
        if path:
            found.append({"name": name, "path": path, "multicolor": name in MULTICOLOR_SLICERS})
    return found

def open_in_slicer(file_path, slicer_name):
    """Open a model file in the specified slicer."""
    info = SLICER_MAP.get(slicer_name, {})
    system = platform.system()
    abs_path = os.path.abspath(file_path)
    if system == "Darwin":
        app = info.get("mac_app", slicer_name)
        subprocess.run(["open", "-a", app, abs_path])
    elif system == "Windows":
        exe = info.get("win_exe")
        exe_path = shutil.which(exe) if exe else None
        if exe_path:
            subprocess.Popen([exe_path, abs_path])
        else:
            os.startfile(abs_path)
    else:
        exe = info.get("linux_exe")
        exe_path = shutil.which(exe) if exe else None
        if exe_path:
            subprocess.Popen([exe_path, abs_path])
        else:
            subprocess.run(["xdg-open", abs_path])
    print(f"Opened {abs_path} in {slicer_name}")

# --- Detect slicers ---
slicers = detect_slicers()
if slicers:
    print("Installed slicers:")
    for s in slicers:
        mc = " [multicolor]" if s["multicolor"] else ""
        print(f"  - {s['name']}{mc}: {s['path']}")
else:
    print("No slicer software detected. Install one of: OrcaSlicer, Bambu Studio, PrusaSlicer, etc.")

White Model Print Pipeline

StepActionCreditsNotes
1Detect installed slicers0Run slicer detection script
2Generate untextured model5–20Text to 3D or Image to 3D (
should_texture: False
)
3Download OBJ0OBJ format for slicer compatibility
4Fix OBJ for printing0Coordinate conversion (see below)
5Open in slicer0
open_in_slicer(obj_path, slicer_name)

White Model Generation + Print Script

Use the

create_task
/
poll_task
/
download
/
get_project_dir
helpers from
meshy-3d-generation
, then:

# --- Step 2: Generate untextured model for printing ---
# Text to 3D:
task_id = create_task("/openapi/v2/text-to-3d", {
    "mode": "preview",
    "prompt": "USER_PROMPT",
    "ai_model": "latest",
    "target_formats": ["obj"],  # Only OBJ for white model printing
})
# OR Image to 3D:
# task_id = create_task("/openapi/v1/image-to-3d", {
#     "image_url": "IMAGE_URL",
#     "should_texture": False,          # White model — no texture
#     "target_formats": ["glb", "obj"], # OBJ needed for slicer
# })

task = poll_task("/openapi/v2/text-to-3d", task_id)  # adjust endpoint for image-to-3d
project_dir = get_project_dir(task_id, task.get("prompt", "print"))

# --- Step 3-4: Download OBJ + fix for printing ---
obj_url = task["model_urls"].get("obj")
if not obj_url:
    print("OBJ format not available. Available:", list(task["model_urls"].keys()))
    print("Download GLB and import manually into your slicer.")
    obj_url = task["model_urls"].get("glb")

obj_path = os.path.join(project_dir, "model.obj")
download(obj_url, obj_path)

# --- Post-process OBJ for slicer compatibility ---
def fix_obj_for_printing(input_path, output_path=None, target_height_mm=75.0):
    """
    Fix OBJ coordinate system, scale, and position for 3D printing slicers.
    - Rotates from glTF Y-up to slicer Z-up: (x, y, z) -> (x, -z, y)
    - Scales model to target_height_mm (default 75mm)
    - Centers model on XY plane
    - Aligns model bottom to Z=0
    """
    if output_path is None:
        output_path = input_path

    lines = open(input_path, "r").readlines()

    rotated = []
    min_x, max_x = float("inf"), float("-inf")
    min_y, max_y = float("inf"), float("-inf")
    min_z, max_z = float("inf"), float("-inf")
    for line in lines:
        if line.startswith("v "):
            parts = line.split()
            x, y, z = float(parts[1]), float(parts[2]), float(parts[3])
            rx, ry, rz = x, -z, y
            min_x, max_x = min(min_x, rx), max(max_x, rx)
            min_y, max_y = min(min_y, ry), max(max_y, ry)
            min_z, max_z = min(min_z, rz), max(max_z, rz)
            rotated.append(("v", rx, ry, rz, parts[4:]))
        elif line.startswith("vn "):
            parts = line.split()
            nx, ny, nz = float(parts[1]), float(parts[2]), float(parts[3])
            rotated.append(("vn", nx, -nz, ny, []))
        else:
            rotated.append(("line", line))

    model_height = max_z - min_z
    scale = target_height_mm / model_height if model_height > 1e-6 else 1.0
    x_offset = -(min_x + max_x) / 2.0 * scale
    y_offset = -(min_y + max_y) / 2.0 * scale
    z_offset = -(min_z * scale)

    with open(output_path, "w") as f:
        for item in rotated:
            if item[0] == "v":
                _, rx, ry, rz, extra = item
                tx = rx * scale + x_offset
                ty = ry * scale + y_offset
                tz = rz * scale + z_offset
                extra_str = " " + " ".join(extra) if extra else ""
                f.write(f"v {tx:.6f} {ty:.6f} {tz:.6f}{extra_str}\n")
            elif item[0] == "vn":
                _, nx, ny, nz, _ = item
                f.write(f"vn {nx:.6f} {ny:.6f} {nz:.6f}\n")
            else:
                f.write(item[1])

    print(f"OBJ fixed: rotated Y-up→Z-up, scaled to {target_height_mm:.0f}mm, centered, bottom at Z=0")
    print(f"Output: {os.path.abspath(output_path)}")

fix_obj_for_printing(obj_path, target_height_mm=75.0)

# --- Open in slicer ---
if slicers:
    open_in_slicer(obj_path, slicers[0]["name"])
else:
    print(f"\nModel ready: {os.path.abspath(obj_path)}")
    print("Open this file in your preferred slicer: File → Import / Open")

Parameters:

  • target_height_mm
    : Default 75mm. Adjust based on user's request (e.g. "print at 15cm" →
    150.0
    ).

Multicolor Print Pipeline

StepActionCreditsNotes
1Detect slicers + check multicolor0Warn if no multicolor slicer
2Generate 3D model20Text to 3D or Image to 3D
3Add textures10Refine or Retexture (REQUIRED)
4Multi-color processing10POST /openapi/v1/print/multi-color
5Poll until SUCCEEDED0GET /openapi/v1/print/multi-color/{id}
6Download 3MF0From model_urls["3mf"]
7Open in multicolor slicer0
open_in_slicer(path, slicer)
Total40

Multi-Color Full Script

Use the

create_task
/
poll_task
/
download
/
get_project_dir
helpers from
meshy-3d-generation
:

# --- Step 1: Check for multicolor slicer (already done above) ---
mc_slicers = [s for s in slicers if s["multicolor"]]
if not mc_slicers:
    print("WARNING: No multicolor-capable slicer detected.")
    print("Supported: OrcaSlicer, Bambu Studio, Creality Print, Elegoo Slicer, Anycubic Slicer Next")
    print("Install one before proceeding.")
else:
    print(f"Multicolor slicer(s): {', '.join(s['name'] for s in mc_slicers)}")

# --- Step 2-3: Generate + texture (with 3mf in target_formats!) ---
# Text to 3D preview:
preview_id = create_task("/openapi/v2/text-to-3d", {
    "mode": "preview",
    "prompt": "USER_PROMPT",
    "ai_model": "latest",
    # No target_formats needed — 3MF comes from the multi-color API, not from generate/refine
})
poll_task("/openapi/v2/text-to-3d", preview_id)

# Refine (add textures — REQUIRED for multicolor):
refine_id = create_task("/openapi/v2/text-to-3d", {
    "mode": "refine",
    "preview_task_id": preview_id,
    "enable_pbr": True,
})
refine_task = poll_task("/openapi/v2/text-to-3d", refine_id)
project_dir = get_project_dir(preview_id, "multicolor-print")

# OR for Image to 3D with texture:
# task_id = create_task("/openapi/v1/image-to-3d", {
#     "image_url": "IMAGE_URL",
#     "should_texture": True,
#     # No target_formats needed — 3MF comes from multi-color API
# })
# refine_task = poll_task("/openapi/v1/image-to-3d", task_id)

INPUT_TASK_ID = refine_id  # Use the textured task
MAX_COLORS = 4   # 1-16, ask user
MAX_DEPTH = 4    # 3-6, ask user

mc_task_id = create_task("/openapi/v1/print/multi-color", {
    "input_task_id": INPUT_TASK_ID,
    "max_colors": MAX_COLORS,
    "max_depth": MAX_DEPTH,
})
print(f"Multi-color task created: {mc_task_id} (10 credits)")

task = poll_task("/openapi/v1/print/multi-color", mc_task_id)

# --- Download 3MF ---
threemf_url = task["model_urls"]["3mf"]
threemf_path = os.path.join(project_dir, "multicolor.3mf")
download(threemf_url, threemf_path)
print(f"3MF ready: {os.path.abspath(threemf_path)}")

# --- Open in multicolor slicer ---
if mc_slicers:
    open_in_slicer(threemf_path, mc_slicers[0]["name"])
else:
    print(f"Open {threemf_path} in a multicolor-capable slicer manually.")

Printability Checklist (Manual Review)

Note: Automated printability analysis API is coming soon. For now, manually review the checklist below before printing.

CheckRecommendation
Wall thicknessMinimum 1.2mm for FDM, 0.8mm for resin
OverhangsKeep below 45° or add supports
Manifold meshEnsure watertight with no holes
Minimum detailAt least 0.4mm for FDM, 0.05mm for resin
Base stabilityFlat base or add brim/raft in slicer
Floating partsAll parts connected or printed separately

Recommendations: Import into your slicer to check for mesh errors. Use the slicer's built-in repair tool if needed. Consider hollowing figurines to save material.


Key Rules for Print Workflow

  • White model: Download OBJ format, apply
    fix_obj_for_printing()
    for coordinate conversion
  • Multicolor: The multi-color API outputs 3MF directly — no coordinate conversion needed (3MF uses Z-up natively)
  • 3MF for multicolor: The Multi-Color Print API outputs 3MF directly — no need to request 3MF from generate/refine via
    target_formats
    . For non-print use cases that need 3MF, pass
    "3mf"
    in
    target_formats
    at generation time.
  • Always detect slicer first and report results to the user before proceeding
  • For multicolor, verify slicer supports it before proceeding with the (costly) pipeline
  • After opening in slicer, remind user to check print settings (layer height, infill, supports)
  • If OBJ is not available: Download GLB and guide user to import manually

Coming Soon

  • Printability Analysis & Fix API — Automated mesh analysis and repair (non-manifold edges, thin walls, floating parts)

Additional Resources

For the complete API endpoint reference, read reference.md.