Grotti 3.5.1: MCP-Anweisung für Clients, Queue-Strip nur bei Ablauf, Start wartet auf printing.
This commit is contained in:
1 parent
f034c5e626
commit
5166ed6530
5 files changed
+60
-16
No files matched your search
@@ -4,8 +4,11 @@ Alle Änderungen an Grotti. Format nach [Keep a Changelog](https://keepachangelo
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [3.5.1] – 2026-10-20
|
||||
|
||||
### Geändert
|
||||
|
||||
- **MCP:** Server-Anweisung für Clients (Ablauf, confirm, Filament-Pause, Platten-Quittung, kein G-code), damit der Agent die Tools in der richtigen Reihenfolge nutzt.
|
||||
- **Wand:** „Aus der Warteschlange“-Strip nur noch bei Ablauf (mit Schritten); einfacher Queue-Druck zeigt nur DRUCKT.
|
||||
|
||||
### Behoben
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://git.gundel.dev/dg/grotti"><img src="https://img.shields.io/badge/Version-3.5.0-e85d04?style=flat-square" alt="Version 3.5.0"></a>
|
||||
<a href="https://git.gundel.dev/dg/grotti"><img src="https://img.shields.io/badge/Version-3.5.1-e85d04?style=flat-square" alt="Version 3.5.1"></a>
|
||||
<img src="https://img.shields.io/badge/Python-3.11+-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python 3.11+">
|
||||
<img src="https://img.shields.io/badge/PostgreSQL-14+-4169E1?style=flat-square&logo=postgresql&logoColor=white" alt="PostgreSQL">
|
||||
<img src="https://img.shields.io/badge/Moonraker-Klipper-2ecc71?style=flat-square" alt="Moonraker">
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# MCP-Server
|
||||
|
||||
Stand **15.10.2026**. Agenten steuern Grotti nur über die HTTP-API mit einem API-Token.
|
||||
Stand **19.10.2026**. Agenten steuern Grotti nur über die HTTP-API mit einem API-Token.
|
||||
|
||||
Beim Connect bekommt der Client die Server-Anweisung aus `grotti/mcp_server.py` (`INSTRUCTIONS`): Deutsch antworten, Host und Datei-id nachschlagen, erst lesen dann schreiben, `confirm=true` nur auf klaren Wunsch, Warteschlange vor Sofortstart, Filament-Pause nicht blind fortsetzen, `clear_bed` ist nur eine Quittung, kein G-code und kein Firmware-Restart.
|
||||
|
||||
## Start
|
||||
|
||||
|
||||
+52
-13
@@ -57,6 +57,51 @@ def _write_guard():
|
||||
raise RuntimeError("MCP läuft im Read-only-Modus (GROTTI_MCP_READ_ONLY).")
|
||||
|
||||
|
||||
# Wird beim Connect an den Client gegeben (Claude, Cursor, …).
|
||||
INSTRUCTIONS = """\
|
||||
Grotti steuert eine 3D-Drucker-Farm (Anycubic Kobra, Klipper/Moonraker).
|
||||
Antworte dem Nutzer auf Deutsch. Handle nur über diese Tools.
|
||||
|
||||
Identität
|
||||
- Drucker über das Feld host (IP). Den Namen nur zur Anzeige nutzen. Hosts nie raten: erst list_printers oder get_printer.
|
||||
- Dateien über die numerische id aus list_files (Feld id), nicht über den Dateinamen allein.
|
||||
- Vor jedem Schreibzugriff den aktuellen Stand lesen.
|
||||
|
||||
Lesen
|
||||
- Farm: list_printers (live=true). Ein Drucker: get_printer(host).
|
||||
- Läuft: list_running. Warteschlange: list_queue. Ereignisse: list_events.
|
||||
- Bibliothek: list_files. Spulen: list_spools. Filament-Fehlmenge: bedarf_lesen.
|
||||
- Kundenaufträge: auftraege_lesen. Verschleißteile: wartung_lesen.
|
||||
|
||||
Zustand (Feld state)
|
||||
- printing: druckt. paused: Pause. standby: frei. complete: fertig, Platte oft noch belegt.
|
||||
- error: Fehler, nicht einfach neu starten. offline: nicht erreichbar.
|
||||
- progress ist 0…1. filament_empty oder pause_reason bei Pause heißt Filament leer.
|
||||
Dann dem Nutzer sagen, die neue Spule in Grotti dem Drucker zuzuweisen, und erst danach resume_printer.
|
||||
Eine Filament-Pause nicht blind fortsetzen.
|
||||
|
||||
Schreiben
|
||||
confirm=true nur setzen, wenn der Nutzer diese Aktion in diesem Zug klar will. Nicht vorauseilend bestätigen.
|
||||
- enqueue_when_free: Datei in die Warteschlange. host leer = Grotti wählt den nächsten Drucker mit passender Sorte. host gesetzt = dieser Drucker, sobald er frei ist und die Platte klar ist. Das ist der normale Weg.
|
||||
- enqueue_print: sofort senden und starten. Nur wenn der Nutzer einen Sofortstart auf einem genannten freien Drucker will (state standby, Platte frei). Große Dateien brauchen oft etwa eine Minute bis zum Start.
|
||||
- Den Material-Match macht Grotti (PLA Meta ist nicht Matte und nicht Silk). Eine falsche Maschine nicht erzwingen. Eine Ablehnung wörtlich weitergeben.
|
||||
- bed_leveling=true nur auf ausdrücklichen Wunsch (Bettnetz / Auto-Level).
|
||||
- pause_printer und resume_printer während des Drucks, ohne confirm.
|
||||
- cancel_printer bricht den Druck ab und braucht confirm=true. Bei Unklarheit erst nachfragen.
|
||||
- clear_bed ist nur die Quittung „Platte ist leer“ und schiebt nichts physisch. Erst aufrufen, wenn der Nutzer sagt, das Teil ist herunter. Ohne diese Quittung startet der nächste „wenn frei“-Job nicht.
|
||||
- steer_temp (Düse und Bett in °C), steer_speed (Prozent), steer_fan (0–100): nur diese Werte. Kein freier G-code.
|
||||
|
||||
Grenzen
|
||||
- Keine G-code-Konsole, kein Firmware-Restart, kein E-Stop, keine Benutzer- oder Token-Verwaltung. Diese Lücken nicht umgehen.
|
||||
- Im Read-only-Modus (GROTTI_MCP_READ_ONLY) schlagen Schreib-Tools fehl. Das sagen und nicht wiederholen.
|
||||
- Gesperrte Drucker und ausgeschaltete Automatik starten keine Warteschlangen-Jobs. Den Fehler nennen.
|
||||
|
||||
Antworten
|
||||
- Kurz und konkret: Druckername, host, Datei, Zustand, was du getan hast.
|
||||
- Den Fehlertext von Grotti zitieren. Keinen Workaround außerhalb der Tools erfinden.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
@@ -67,23 +112,17 @@ def main():
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
server = FastMCP(
|
||||
"grotti",
|
||||
instructions=(
|
||||
"Grotti steuert die 3D-Drucker-Farm. Destruktive Aktionen brauchen "
|
||||
"confirm=true. Kein freier G-code, kein Firmware-Restart."
|
||||
),
|
||||
)
|
||||
server = FastMCP("grotti", instructions=INSTRUCTIONS)
|
||||
|
||||
@server.tool()
|
||||
def list_printers(live: bool = True) -> str:
|
||||
"""Druckerliste mit optionalem Live-Zustand."""
|
||||
"""Alle Drucker. live=true liefert Zustand, Temps, Fortschritt, Dateiname. Host ist die IP."""
|
||||
q = "?live=1" if live else "?live=0"
|
||||
return json.dumps(_api("GET", "/drucker" + q), ensure_ascii=False, indent=2)
|
||||
|
||||
@server.tool()
|
||||
def get_printer(host: str) -> str:
|
||||
"""Einen Drucker anhand Host/IP aus der Live-Liste."""
|
||||
"""Einen Drucker anhand host (IP) aus der Live-Liste. Host vorher über list_printers holen."""
|
||||
rows = _api("GET", "/drucker?live=1") or []
|
||||
for row in rows:
|
||||
if row.get("host") == host:
|
||||
@@ -115,7 +154,7 @@ def main():
|
||||
|
||||
@server.tool()
|
||||
def list_files(limit: int = 50) -> str:
|
||||
"""Dateien in der Bibliothek."""
|
||||
"""Dateien in der Bibliothek. Die numerische id ist die file_id fürs Einreihen und Starten."""
|
||||
rows = _api("GET", "/dateien") or []
|
||||
return json.dumps(rows[: max(1, min(limit, 200))], ensure_ascii=False, indent=2)
|
||||
|
||||
@@ -181,7 +220,7 @@ def main():
|
||||
|
||||
@server.tool()
|
||||
def clear_bed(host: str, confirm: bool = False) -> str:
|
||||
"""Platte als geräumt quittieren (kein physisches Schieben)."""
|
||||
"""Quittung, dass der Nutzer die Platte geleert hat. Schiebt nichts. confirm muss true sein. Danach kann die Warteschlange den nächsten Job starten."""
|
||||
_write_guard()
|
||||
if not confirm:
|
||||
raise RuntimeError("Quittierung braucht confirm=true.")
|
||||
@@ -201,7 +240,7 @@ def main():
|
||||
confirm: bool = False,
|
||||
bed_leveling: bool = False,
|
||||
) -> str:
|
||||
"""Datei in die Warteschlange („wenn frei“). confirm muss true sein."""
|
||||
"""Datei in die Warteschlange. host leer: Grotti wählt den nächsten Drucker mit passender Sorte. host gesetzt: dieser Drucker, sobald frei und Platte klar. confirm muss true sein. Normaler Weg zum Drucken."""
|
||||
_write_guard()
|
||||
if not confirm:
|
||||
raise RuntimeError("Einreihen braucht confirm=true.")
|
||||
@@ -220,7 +259,7 @@ def main():
|
||||
confirm: bool = False,
|
||||
bed_leveling: bool = False,
|
||||
) -> str:
|
||||
"""Sofort senden und starten. confirm muss true sein."""
|
||||
"""Sofort an diesen host senden und starten. Nur bei freiem Drucker (standby, Platte frei) und klarem Sofortstart-Wunsch. confirm muss true sein."""
|
||||
_write_guard()
|
||||
if not confirm:
|
||||
raise RuntimeError("Start braucht confirm=true.")
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
"""Version von Grotti (SemVer). Bei jeder Änderung CHANGELOG.md mitführen."""
|
||||
|
||||
__version__ = "3.5.0"
|
||||
__version__ = "3.5.1"
|
||||
Reference in new issue
Block a user