Initial commit: OpenSCAD MCP server for Claude Code
MCP server that lets Claude generate and render OpenSCAD models to STL files. Includes setup documentation and installation guide. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
OpenSCAD MCP Server
|
||||
Ermöglicht Claude, OpenSCAD-Code zu rendern und STL-Dateien zu erzeugen.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import tempfile
|
||||
import os
|
||||
import base64
|
||||
from pathlib import Path
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
# Pfad zur OpenSCAD-Executable (Windows-Standard)
|
||||
OPENSCAD_PATH = os.environ.get(
|
||||
"OPENSCAD_PATH",
|
||||
r"C:\Program Files\OpenSCAD\openscad.exe"
|
||||
)
|
||||
|
||||
# Output-Verzeichnis für STL-Dateien
|
||||
OUTPUT_DIR = Path(os.environ.get("OPENSCAD_OUTPUT_DIR", Path.home() / "3d-models"))
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mcp = FastMCP("openscad")
|
||||
|
||||
|
||||
def _run_openscad(scad_code: str, output_path: Path, extra_args: list = None) -> tuple[bool, str]:
|
||||
"""Führt OpenSCAD mit dem gegebenen Code aus. Gibt (success, output) zurück."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".scad", mode="w", delete=False, encoding="utf-8") as f:
|
||||
f.write(scad_code)
|
||||
scad_file = f.name
|
||||
|
||||
try:
|
||||
cmd = [OPENSCAD_PATH, "-o", str(output_path)] + (extra_args or []) + [scad_file]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
||||
output = (result.stdout + result.stderr).strip()
|
||||
success = result.returncode == 0 and output_path.exists()
|
||||
return success, output
|
||||
finally:
|
||||
os.unlink(scad_file)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def render_stl(code: str, filename: str = "model.stl") -> str:
|
||||
"""
|
||||
Rendert OpenSCAD-Code als STL-Datei für den 3D-Drucker.
|
||||
|
||||
Args:
|
||||
code: Gültiger OpenSCAD-Code
|
||||
filename: Dateiname für die STL-Ausgabe (z.B. "halter.stl")
|
||||
|
||||
Returns:
|
||||
Pfad zur erzeugten STL-Datei oder Fehlermeldung
|
||||
"""
|
||||
if not filename.endswith(".stl"):
|
||||
filename += ".stl"
|
||||
|
||||
output_path = OUTPUT_DIR / filename
|
||||
success, output = _run_openscad(code, output_path)
|
||||
|
||||
if success:
|
||||
size_kb = output_path.stat().st_size // 1024
|
||||
return f"STL erfolgreich erstellt: {output_path}\nDateigröße: {size_kb} KB\nOpenSCAD-Output: {output or '(kein Output)'}"
|
||||
else:
|
||||
return f"Fehler beim Rendern:\n{output}\n\nBitte OpenSCAD-Code prüfen."
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def preview_png(code: str, filename: str = "preview.png") -> str:
|
||||
"""
|
||||
Rendert eine PNG-Vorschau des OpenSCAD-Modells.
|
||||
|
||||
Args:
|
||||
code: Gültiger OpenSCAD-Code
|
||||
filename: Dateiname für die PNG-Ausgabe
|
||||
|
||||
Returns:
|
||||
Base64-kodiertes PNG-Bild oder Fehlermeldung
|
||||
"""
|
||||
if not filename.endswith(".png"):
|
||||
filename += ".png"
|
||||
|
||||
output_path = OUTPUT_DIR / filename
|
||||
# --camera=0,0,0,55,0,25,140 = isometrische Ansicht
|
||||
extra = ["--camera=0,0,0,55,0,25,140", "--imgsize=800,600", "--colorscheme=DeepOcean"]
|
||||
success, output = _run_openscad(code, output_path, extra_args=extra)
|
||||
|
||||
if success:
|
||||
with open(output_path, "rb") as f:
|
||||
img_b64 = base64.b64encode(f.read()).decode()
|
||||
return f"data:image/png;base64,{img_b64}"
|
||||
else:
|
||||
return f"Fehler beim Rendern der Vorschau:\n{output}"
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def list_models() -> str:
|
||||
"""Listet alle bisher erstellten 3D-Modelle im Output-Verzeichnis."""
|
||||
files = list(OUTPUT_DIR.glob("*.stl"))
|
||||
if not files:
|
||||
return f"Keine STL-Dateien in {OUTPUT_DIR}"
|
||||
lines = [f"Modelle in {OUTPUT_DIR}:"]
|
||||
for f in sorted(files):
|
||||
size_kb = f.stat().st_size // 1024
|
||||
lines.append(f" {f.name} ({size_kb} KB)")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
Reference in New Issue
Block a user