# OpenSCAD MCP Server — Installationsanleitung LLM-gestützte 3D-Modell-Erstellung: Claude generiert OpenSCAD-Code und rendert ihn direkt zu STL-Dateien für den 3D-Drucker. --- ## Voraussetzungen - Windows 10/11 - Claude Code Desktop App installiert - Python (Microsoft Store) — bereits installiert auf diesem System --- ## Schritt 1: OpenSCAD installieren 1. https://openscad.org/downloads.html aufrufen 2. **Windows Installer** herunterladen und ausführen 3. Standardpfad bestätigen: `C:\Program Files\OpenSCAD\` Verifizieren: ``` "C:\Program Files\OpenSCAD\openscad.exe" --version ``` Erwartete Ausgabe: `OpenSCAD version 2021.01` (oder neuer) --- ## Schritt 2: Projektverzeichnis anlegen ``` mkdir C:\Users\\Documents\Claude\openscad-mcp ``` --- ## Schritt 3: Python Virtual Environment erstellen > **Wichtig:** Den Microsoft Store Python verwenden (`python3.13.exe` in WindowsApps). > Das `python.exe` aus `C:\Users\...\AppData\Local\Programs\Python\Python313\` ist ein > eingebettetes Python **ohne pip** und funktioniert nicht. > Die `WindowsApps\python3.13.exe` ist ein App Execution Alias — funktioniert nur in > interaktiven Shells, **nicht** als Subprocess von Claude Code. > Lösung: Virtual Environment erstellen — das `.venv\Scripts\python.exe` ist eine > echte Executable ohne diese Einschränkung. ``` cd C:\Users\\Documents\Claude\openscad-mcp "C:\Users\\AppData\Local\Microsoft\WindowsApps\python3.13.exe" -m venv .venv .venv\Scripts\pip install mcp ``` Verifizieren: ``` .venv\Scripts\python.exe -c "from mcp.server.fastmcp import FastMCP; print('OK')" ``` --- ## Schritt 4: MCP-Server erstellen Datei `C:\Users\\Documents\Claude\openscad-mcp\server.py`: ```python """ 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 OPENSCAD_PATH = os.environ.get( "OPENSCAD_PATH", r"C:\Program Files\OpenSCAD\openscad.exe" ) 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]: 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.""" 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}" @mcp.tool() def preview_png(code: str, filename: str = "preview.png") -> str: """Rendert eine PNG-Vorschau des OpenSCAD-Modells.""" if not filename.endswith(".png"): filename += ".png" output_path = OUTPUT_DIR / filename 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() ``` --- ## Schritt 5: MCP-Server testen ``` echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | .venv\Scripts\python.exe server.py ``` Erwartete Ausgabe: JSON-Antwort mit `"name":"openscad"` — Server funktioniert. --- ## Schritt 6: Claude Code Desktop konfigurieren > **Wichtig:** Es gibt zwei Settings-Dateien. Nur eine davon wird von der Desktop App gelesen: > > | Datei | Gelesen von | > |-------|------------| > | `C:\Users\\.claude\settings.json` | Claude Code **CLI** (Terminal) | > | `C:\Users\\AppData\Roaming\Claude\claude_desktop_config.json` | Claude Code **Desktop App** | > > Da die Desktop App verwendet wird, muss der Eintrag in `claude_desktop_config.json`. Datei öffnen: `C:\Users\\AppData\Roaming\Claude\claude_desktop_config.json` Den `mcpServers`-Block hinzufügen (**Backslashes** im Pfad beachten): ```json { "mcpServers": { "openscad": { "command": "C:\\Users\\\\Documents\\Claude\\openscad-mcp\\.venv\\Scripts\\python.exe", "args": ["C:\\Users\\\\Documents\\Claude\\openscad-mcp\\server.py"] } }, "preferences": { ...vorhandene Einstellungen beibehalten... } } ``` --- ## Schritt 7: Claude Code neu starten **Vollständig beenden** — nicht nur neues Chat-Fenster öffnen: - Rechtsklick auf Claude-Icon im **Systemtray** → **Quit/Beenden** - Alternativ: Task-Manager → `claude.exe` beenden Dann Claude Code neu starten und einen neuen Chat öffnen. --- ## Verifikation In einem neuen Chat eingeben: > "Erstelle einen einfachen Würfel 20x20x20mm als test.stl" Claude sollte automatisch das Tool `render_stl` aufrufen und eine STL-Datei in `C:\Users\\3d-models\test.stl` erzeugen. --- ## Beispiel-Prompts ``` Erstelle einen Filamentrollenhalter für 200mm Außendurchmesser, 30mm Achse, als filamenthalter.stl ``` ``` Erstelle einen Wandhalter für ein 32mm Rohr, Wandstärke 3mm, 2 Schraubenlöcher M4, als rohrhalter.stl ``` ``` Erstelle eine Box 80x50x30mm mit Deckel und Scharnier, Wandstärke 2mm, als box.stl ``` --- ## Troubleshooting ### Server wird nicht geladen (Tools nicht sichtbar) - Prüfen ob Eintrag in `claude_desktop_config.json` ist (nicht in `settings.json`) - Claude Code **vollständig** neu starten (Systemtray → Quit) - Debug-Log prüfen: `C:\Users\\.claude\debug\` — neueste Datei nach `openscad` durchsuchen ### STL-Render schlägt fehl - OpenSCAD-Pfad prüfen: `"C:\Program Files\OpenSCAD\openscad.exe" --version` - Output-Verzeichnis prüfen: `C:\Users\\3d-models\` muss existieren (wird automatisch erstellt) ### Python/mcp Import-Fehler - Virtual Environment neu erstellen (Schritt 3) - Prüfen: `.venv\Scripts\python.exe -c "from mcp.server.fastmcp import FastMCP; print('OK')"` --- ## Dateistruktur ``` C:\Users\\ ├── AppData\Roaming\Claude\ │ └── claude_desktop_config.json ← MCP-Server Registrierung ├── .claude\ │ └── settings.json ← Claude Code CLI-Settings (nicht relevant) ├── Documents\Claude\openscad-mcp\ │ ├── .venv\ ← Python Virtual Environment │ │ └── Scripts\python.exe ← Echter Python-Interpreter │ └── server.py ← MCP-Server Code └── 3d-models\ ← Ausgabeverzeichnis für STL-Dateien ├── filamenthalter.stl └── ... ``` --- ## Hintergrund: Warum so kompliziert? 1. **Zwei Config-Dateien**: Claude Code Desktop liest `claude_desktop_config.json`, nicht `~/.claude/settings.json`. Letzteres ist nur für den CLI-Modus. 2. **Windows App Execution Aliases**: `WindowsApps\python3.13.exe` ist ein Stub der nur aus interaktiven Shells funktioniert. Als Subprocess von Claude Code schlägt er ohne Fehlermeldung fehl. Ein Virtual Environment erstellt eine echte `python.exe` ohne diese Einschränkung. 3. **Eingebettetes Python**: `C:\...\Programs\Python\Python313\python.exe` (das vom `py`-Launcher verwendete) hat kein `pip` und kann nicht für MCP verwendet werden.