Files
cadquery/server.py
T
nova bae8071e69 Update config, docs and add box model
- server.py: Output-Verzeichnis default auf models/ relativ zu server.py
- README.md: Claude Code statt Desktop, models/-Ordner, korrekte settings.json
- models/box.py: Offene Box 30x30x20mm, alle Maße als Variablen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:29:23 +01:00

125 lines
3.5 KiB
Python

"""
CadQuery MCP Server
Ermöglicht Claude, CadQuery-Python-Code auszuführen und STL-Dateien zu erzeugen.
"""
import os
import sys
import tempfile
import base64
import traceback
from pathlib import Path
from mcp.server.fastmcp import FastMCP
import cadquery as cq
from cadquery import exporters
_default_output = Path(__file__).parent / "models"
OUTPUT_DIR = Path(os.environ.get("CADQUERY_OUTPUT_DIR", _default_output))
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
mcp = FastMCP("cadquery")
def _run_cadquery(code: str) -> cq.Workplane:
"""Führt CadQuery-Code aus und gibt das Ergebnis zurück."""
captured = {}
def show_object(obj, name="result", options=None):
captured["result"] = obj
def show(obj, name="result", options=None):
captured["result"] = obj
namespace = {
"cq": cq,
"cadquery": cq,
"show_object": show_object,
"show": show,
}
exec(code, namespace) # noqa: S102
if "result" in captured:
return captured["result"]
# Letzte Variable suchen, die ein Workplane oder Shape ist
for val in reversed(list(namespace.values())):
if isinstance(val, (cq.Workplane, cq.Shape, cq.Assembly)):
return val
raise ValueError(
"Kein CadQuery-Objekt gefunden. "
"Bitte 'show_object(result)' am Ende des Codes aufrufen "
"oder das Ergebnis in eine Variable namens 'result' speichern."
)
@mcp.tool()
def render_stl(code: str, filename: str = "model.stl") -> str:
"""
Rendert CadQuery-Python-Code als STL-Datei für den 3D-Drucker.
Args:
code: Gültiger CadQuery-Python-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
try:
result = _run_cadquery(code)
exporters.export(result, str(output_path))
size_kb = output_path.stat().st_size // 1024
return f"STL erfolgreich erstellt: {output_path}\nDateigröße: {size_kb} KB"
except Exception as e:
return f"Fehler beim Rendern:\n{traceback.format_exc()}\nBitte CadQuery-Code prüfen."
@mcp.tool()
def preview_png(code: str, filename: str = "preview.png") -> str:
"""
Rendert eine PNG-Vorschau des CadQuery-Modells.
Args:
code: Gültiger CadQuery-Python-Code
filename: Dateiname für die PNG-Ausgabe
Returns:
Base64-kodiertes PNG-Bild oder Fehlermeldung
"""
if not filename.endswith(".png"):
filename += ".png"
svg_path = OUTPUT_DIR / filename.replace(".png", ".svg")
output_path = OUTPUT_DIR / filename
try:
result = _run_cadquery(code)
exporters.export(result, str(svg_path))
size_bytes = svg_path.stat().st_size
return f"SVG-Vorschau erstellt: {svg_path} ({size_bytes} Bytes)\n(PNG-Konvertierung erfordert zusätzliche Abhängigkeiten)"
except Exception as e:
return f"Fehler beim Rendern der Vorschau:\n{traceback.format_exc()}"
@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()