Initial commit: CadQuery MCP Server
- server.py: FastMCP-Server mit render_stl, preview_png, list_models - README.md: Ausführliche Installationsanleitung (DE) - .gitignore: Schließt .venv, STL/PNG-Ausgaben und Cache aus Stack: cadquery 2.7.0, mcp 1.26.0, cq-editor 0.6.2, Python 3.13 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Python
|
||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# Generierte Dateien
|
||||||
|
*.stl
|
||||||
|
*.step
|
||||||
|
*.svg
|
||||||
|
*.png
|
||||||
|
mcp_server.log
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
Thumbs.db
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
# CadQuery MCP Server
|
||||||
|
|
||||||
|
Ein MCP-Server (Model Context Protocol), der Claude ermöglicht, parametrische
|
||||||
|
3D-Modelle direkt über CadQuery-Python-Code zu erstellen und als STL-Dateien zu exportieren.
|
||||||
|
Zusätzlich ist CQ-editor als GUI-Vorschau-Tool integriert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Inhaltsverzeichnis
|
||||||
|
|
||||||
|
1. [Voraussetzungen](#1-voraussetzungen)
|
||||||
|
2. [Installation](#2-installation)
|
||||||
|
3. [Claude Desktop einbinden](#3-claude-desktop-einbinden)
|
||||||
|
4. [CQ-editor starten](#4-cq-editor-starten)
|
||||||
|
5. [Verwendung in Claude](#5-verwendung-in-claude)
|
||||||
|
6. [Projektstruktur](#6-projektstruktur)
|
||||||
|
7. [Fehlerbehebung](#7-fehlerbehebung)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Voraussetzungen
|
||||||
|
|
||||||
|
| Anforderung | Version | Hinweis |
|
||||||
|
|---|---|---|
|
||||||
|
| Windows 10/11 | 64-Bit | Getestet auf Windows 11 |
|
||||||
|
| Python | ≥ 3.10 | [python.org](https://www.python.org/downloads/) – „Add to PATH" aktivieren |
|
||||||
|
| Claude Desktop | aktuell | [claude.ai/download](https://claude.ai/download) |
|
||||||
|
| Git | optional | Für Versionsverwaltung |
|
||||||
|
|
||||||
|
> **Hinweis:** Python 3.13 wird vollständig unterstützt (CadQuery 2.7.0 liefert fertige Wheels).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Installation
|
||||||
|
|
||||||
|
### 2.1 Projektverzeichnis anlegen
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
mkdir C:\Users\<Benutzername>\Documents\Claude\cadquery
|
||||||
|
cd C:\Users\<Benutzername>\Documents\Claude\cadquery
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Python Virtual Environment erstellen
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m venv .venv
|
||||||
|
```
|
||||||
|
|
||||||
|
Das erzeugt einen isolierten Python-Interpreter unter `.venv\`.
|
||||||
|
|
||||||
|
### 2.3 CadQuery, MCP-Server und CQ-editor installieren
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.venv\Scripts\pip install cadquery mcp[cli] cq-editor
|
||||||
|
```
|
||||||
|
|
||||||
|
Installierte Pakete (Auswahl):
|
||||||
|
|
||||||
|
| Paket | Version | Zweck |
|
||||||
|
|---|---|---|
|
||||||
|
| `cadquery` | 2.7.0 | Parametrisches CAD in Python (OpenCASCADE) |
|
||||||
|
| `cadquery-ocp` | 7.8.1 | OpenCASCADE Python-Bindings |
|
||||||
|
| `mcp` | 1.26.0 | Model Context Protocol SDK (FastMCP) |
|
||||||
|
| `cq-editor` | 0.6.2 | GUI-Editor mit Live-Vorschau (PyQt5) |
|
||||||
|
|
||||||
|
> **Downloadgröße:** ca. 150 MB (cadquery-ocp ~52 MB, PyQt5-Qt5 ~50 MB, casadi ~51 MB)
|
||||||
|
> Bei Verbindungsunterbrechung einfach erneut ausführen – pip setzt den Download fort.
|
||||||
|
|
||||||
|
### 2.4 Installation prüfen
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.venv\Scripts\python -c "import cadquery as cq; print('cadquery', cq.__version__)"
|
||||||
|
# Ausgabe: cadquery 2.7.0
|
||||||
|
|
||||||
|
.venv\Scripts\python -c "import cq_editor; print('cq-editor OK')"
|
||||||
|
# Ausgabe: cq-editor OK
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.5 Test-STL erzeugen
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.venv\Scripts\python -c "
|
||||||
|
import cadquery as cq
|
||||||
|
from cadquery import exporters
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
output = Path.home() / '3d-models'
|
||||||
|
output.mkdir(exist_ok=True)
|
||||||
|
result = cq.Workplane('XY').box(20, 20, 10)
|
||||||
|
exporters.export(result, str(output / 'test_box.stl'))
|
||||||
|
print('OK:', output / 'test_box.stl')
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
Die STL-Datei wird in `C:\Users\<Benutzername>\3d-models\` gespeichert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Claude Desktop einbinden
|
||||||
|
|
||||||
|
### 3.1 Konfigurationsdatei öffnen
|
||||||
|
|
||||||
|
```
|
||||||
|
%APPDATA%\Claude\claude_desktop_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Unter Windows typischerweise:
|
||||||
|
```
|
||||||
|
C:\Users\<Benutzername>\AppData\Roaming\Claude\claude_desktop_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 CadQuery-Server eintragen
|
||||||
|
|
||||||
|
Den folgenden Block in `"mcpServers"` einfügen:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"cadquery": {
|
||||||
|
"command": "C:\\Users\\<Benutzername>\\Documents\\Claude\\cadquery\\.venv\\Scripts\\python.exe",
|
||||||
|
"args": ["C:\\Users\\<Benutzername>\\Documents\\Claude\\cadquery\\server.py"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Wichtig:** `<Benutzername>` durch den tatsächlichen Windows-Benutzernamen ersetzen.
|
||||||
|
> Backslashes müssen in JSON doppelt escapt werden (`\\`).
|
||||||
|
|
||||||
|
Vollständiges Beispiel mit OpenSCAD-Server:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"openscad": {
|
||||||
|
"command": "C:\\Users\\thors\\Documents\\Claude\\openscad-mcp\\.venv\\Scripts\\python.exe",
|
||||||
|
"args": ["C:\\Users\\thors\\Documents\\Claude\\openscad-mcp\\server.py"]
|
||||||
|
},
|
||||||
|
"cadquery": {
|
||||||
|
"command": "C:\\Users\\thors\\Documents\\Claude\\cadquery\\.venv\\Scripts\\python.exe",
|
||||||
|
"args": ["C:\\Users\\thors\\Documents\\Claude\\cadquery\\server.py"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 Claude Desktop neu starten
|
||||||
|
|
||||||
|
Nach dem Speichern der Config muss Claude Desktop **vollständig neu gestartet** werden.
|
||||||
|
Die MCP-Tools erscheinen dann automatisch in der Werkzeugliste.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. CQ-editor starten
|
||||||
|
|
||||||
|
CQ-editor ist ein Qt-basierter GUI-Editor mit Live-3D-Vorschau.
|
||||||
|
|
||||||
|
### Starten über die Kommandozeile
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Direkt über die installierte Executable:
|
||||||
|
C:\Users\<Benutzername>\Documents\Claude\cadquery\.venv\Scripts\cq-editor.exe
|
||||||
|
|
||||||
|
# Alternativ über Python:
|
||||||
|
.venv\Scripts\python -m cq_editor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Startskript (optional)
|
||||||
|
|
||||||
|
Für einfacheres Starten kann eine Datei `start-cq-editor.bat` im Projektverzeichnis
|
||||||
|
angelegt werden:
|
||||||
|
|
||||||
|
```batch
|
||||||
|
@echo off
|
||||||
|
C:\Users\<Benutzername>\Documents\Claude\cadquery\.venv\Scripts\cq-editor.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
### Workflow mit CQ-editor
|
||||||
|
|
||||||
|
1. CQ-editor starten
|
||||||
|
2. CadQuery-Code im Editor schreiben
|
||||||
|
3. **F5** drücken → Live-Vorschau rechts aktualisiert sich
|
||||||
|
4. Datei → Export → STL zum Speichern
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Verwendung in Claude
|
||||||
|
|
||||||
|
### 5.1 Verfügbare MCP-Tools
|
||||||
|
|
||||||
|
Nach dem Neustart von Claude Desktop stehen drei Tools zur Verfügung:
|
||||||
|
|
||||||
|
#### `mcp__cadquery__render_stl`
|
||||||
|
Führt CadQuery-Python-Code aus und exportiert eine STL-Datei.
|
||||||
|
|
||||||
|
```
|
||||||
|
Parameter:
|
||||||
|
code : str – CadQuery-Python-Code
|
||||||
|
filename : str – Ausgabedateiname (Standard: "model.stl")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mcp__cadquery__preview_png`
|
||||||
|
Exportiert eine SVG-Vorschau des Modells.
|
||||||
|
|
||||||
|
```
|
||||||
|
Parameter:
|
||||||
|
code : str – CadQuery-Python-Code
|
||||||
|
filename : str – Ausgabedateiname (Standard: "preview.png")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mcp__cadquery__list_models`
|
||||||
|
Listet alle STL-Dateien im Output-Verzeichnis auf.
|
||||||
|
|
||||||
|
### 5.2 Code-Konventionen
|
||||||
|
|
||||||
|
Der MCP-Server erkennt das CadQuery-Ergebnis auf zwei Wegen:
|
||||||
|
|
||||||
|
**Option A — `show_object()` aufrufen (empfohlen, CQ-editor-kompatibel):**
|
||||||
|
```python
|
||||||
|
import cadquery as cq
|
||||||
|
|
||||||
|
result = cq.Workplane("XY").box(20, 20, 10)
|
||||||
|
show_object(result)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B — Variable `result` verwenden:**
|
||||||
|
```python
|
||||||
|
import cadquery as cq
|
||||||
|
|
||||||
|
result = cq.Workplane("XY").box(20, 20, 10)
|
||||||
|
# "result" wird automatisch erkannt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 Beispiele
|
||||||
|
|
||||||
|
**Einfacher Quader:**
|
||||||
|
```python
|
||||||
|
import cadquery as cq
|
||||||
|
|
||||||
|
result = cq.Workplane("XY").box(30, 20, 10)
|
||||||
|
show_object(result)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zylinder mit Bohrung:**
|
||||||
|
```python
|
||||||
|
import cadquery as cq
|
||||||
|
|
||||||
|
result = (
|
||||||
|
cq.Workplane("XY")
|
||||||
|
.cylinder(height=30, radius=15)
|
||||||
|
.faces(">Z")
|
||||||
|
.hole(10)
|
||||||
|
)
|
||||||
|
show_object(result)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parametrisches Modell:**
|
||||||
|
```python
|
||||||
|
import cadquery as cq
|
||||||
|
|
||||||
|
breite = 40
|
||||||
|
hoehe = 20
|
||||||
|
tiefe = 10
|
||||||
|
radius = 3
|
||||||
|
|
||||||
|
result = (
|
||||||
|
cq.Workplane("XY")
|
||||||
|
.box(breite, tiefe, hoehe)
|
||||||
|
.edges("|Z")
|
||||||
|
.fillet(radius)
|
||||||
|
)
|
||||||
|
show_object(result)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Assembly (Baugruppe):**
|
||||||
|
```python
|
||||||
|
import cadquery as cq
|
||||||
|
|
||||||
|
box = cq.Workplane("XY").box(20, 20, 10)
|
||||||
|
zylinder = cq.Workplane("XY").cylinder(15, 5)
|
||||||
|
|
||||||
|
asm = (
|
||||||
|
cq.Assembly()
|
||||||
|
.add(box, name="basis", loc=cq.Location((0, 0, 0)))
|
||||||
|
.add(zylinder, name="zapfen", loc=cq.Location((0, 0, 10)))
|
||||||
|
)
|
||||||
|
show_object(asm)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.4 Output-Verzeichnis
|
||||||
|
|
||||||
|
STL-Dateien werden gespeichert in:
|
||||||
|
```
|
||||||
|
C:\Users\<Benutzername>\3d-models\
|
||||||
|
```
|
||||||
|
|
||||||
|
Diesen Pfad kann man über die Umgebungsvariable `CADQUERY_OUTPUT_DIR` überschreiben.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
cadquery/
|
||||||
|
├── .venv/ # Python Virtual Environment (nicht ins Git)
|
||||||
|
├── server.py # MCP-Server (FastMCP)
|
||||||
|
└── README.md # Diese Datei
|
||||||
|
```
|
||||||
|
|
||||||
|
### server.py – Übersicht
|
||||||
|
|
||||||
|
```
|
||||||
|
_run_cadquery(code) Führt CadQuery-Code aus, fängt show_object() ab
|
||||||
|
render_stl(code, fn) Tool: exportiert STL über cadquery.exporters
|
||||||
|
preview_png(code, fn) Tool: exportiert SVG-Vorschau
|
||||||
|
list_models() Tool: listet STL-Dateien in OUTPUT_DIR
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Fehlerbehebung
|
||||||
|
|
||||||
|
### „Kein CadQuery-Objekt gefunden"
|
||||||
|
|
||||||
|
Der Server konnte kein Ergebnis aus dem Code extrahieren.
|
||||||
|
|
||||||
|
**Lösung:** Am Ende des Codes explizit `show_object(result)` aufrufen oder das Ergebnis
|
||||||
|
in eine Variable namens `result` speichern.
|
||||||
|
|
||||||
|
### STL-Datei wird nicht erstellt
|
||||||
|
|
||||||
|
- Output-Verzeichnis prüfen: `C:\Users\<Benutzername>\3d-models\`
|
||||||
|
- Schreibrechte auf das Verzeichnis prüfen
|
||||||
|
|
||||||
|
### Claude Desktop zeigt keine MCP-Tools
|
||||||
|
|
||||||
|
1. `claude_desktop_config.json` auf korrekte Pfade und JSON-Syntax prüfen
|
||||||
|
2. Claude Desktop vollständig beenden (Taskleiste → Rechtsklick → Beenden)
|
||||||
|
3. Claude Desktop neu starten
|
||||||
|
|
||||||
|
### Download bricht ab (cadquery-ocp / PyQt5-Qt5)
|
||||||
|
|
||||||
|
Beide Pakete sind ca. 50 MB groß. Bei Abbruch einfach wiederholen:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.venv\Scripts\pip install cadquery mcp[cli] cq-editor --retries 5
|
||||||
|
```
|
||||||
|
|
||||||
|
### CQ-editor startet nicht
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Fehlerausgabe anzeigen:
|
||||||
|
.venv\Scripts\python -m cq_editor
|
||||||
|
```
|
||||||
|
|
||||||
|
Häufige Ursache: Fehlende Qt-Laufzeitbibliotheken → Visual C++ Redistributable
|
||||||
|
installieren: [aka.ms/vs/17/release/vc_redist.x64.exe](https://aka.ms/vs/17/release/vc_redist.x64.exe)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
MIT License — freie Nutzung, Modifikation und Weitergabe erlaubt.
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
OUTPUT_DIR = Path(os.environ.get("CADQUERY_OUTPUT_DIR", Path.home() / "3d-models"))
|
||||||
|
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()
|
||||||
Reference in New Issue
Block a user