Ein Min-Master/Slave-Setup, das die teure, in der Wolke laufende Agenten-Instanz
(„Master”) entlastet: Der Master plant, delegiert, verifiziert und committet;
ein günstiger lokaler Slave (Muse Glimmer 28B in LM Studio) erledigt die
eigentliche Implementierungsarbeit nach Möglichkeit selbst. Die Kommunikation
läuft über ein MCP-Gateway auf Port 3000, das den Slave über LM Studios
OpenAI-kompatible API (localhost:1234) adressiert. Kern der Architektur: eine
aufgabengebundene Tool-Freigabe und ein Reasoning-fester Tool-Loop –
beide entscheidend, damit der Slave Werkzeuge tatsächlich zuverlässig aufruft.
Kernregeln (Cheat-Sheet)
Damit die Delegation den Ablauf nicht stört — die wichtigsten Regeln:
Nie parallel delegieren. Mehrere gleichzeitige delegate_to_slave-Calls
teilen sich dieselben GPU-Slots, bremsen sich gegenseitig und führen zu
Timeouts (-32001). Immer sequenziell, ein Call nach dem anderen.
Jeder Aufruf klein halten (≈ 1–3 Tool-Calls, eine konkrete Datei bzw.
Aufgabe). Große mehrschrittige Implementierungen slice-weise delegieren.
Keine künstlichen Token-Limits.max_tokens großzügig (Standard 8192,
bei umfangreichen Tasks höher), Timeout nicht unnötig senken. Zu knappe
Zuteilung bricht die Abarbeitung ab → unvollständiges Ergebnis → doppelte
Kosten durch Neustart. Lieber ein Aufruf mehr als ein abgebrochener.
Gateway-Neustart nur bei nachgewiesenem Hänger. Kein Neustart als
„Re-Init” bei jedem Problem — erst diagnostizieren (Service-Status,
Health-Checks, Log, LM-Studio-Modelle). Jeder Neustart invalidiert die
Client-SSE-Session (-32602 → opencode neu verbinden).
Stand 2026-08-30: Endlosschleifen-Schutz im Slave-Tool-Loop + Wiki-Freigabe
Nach einem Vorfall, bei dem ein delegierter Slave nicht zur finalen Antwort fand,
sondern immer wieder dieselben Tool-Calls emittierte (Stundenlauf bis zum
1-h-Gesamtlimit), wurde modules/orchestrator.py gehärtet:
Wiki-Tools für general-Slave freigeschaltet.wiki_suche, wiki_lesen,
wiki_uebersicht (read-only, SAFE_SLAVE_MODULES) stehen dem general-Typ
jetzt in SLAVE_TOOLS_BY_TYPE zur Verfügung. Vorher gehörten sie in kein
Slave-Tool-Set — ein Slave, der das Wiki lesen sollte, generierte ins Leere,
weil er kein passendes Werkzeug sah.
MAX_STALLED_ROUNDS=3 – identische Wiederholungen brechen sofort ab.
Emittiert das Modell in Folge dieselben Tool-Calls (Name + Argumente), wird
nach 3× abgebrochen („Tool-Schleife ohne Fortschritt"). Das ist das exakte
Endlosschleifen-Bild von glimmer (immer wieder dasselbe nachschlagen).
MAX_TOOL_ONLY_ROUNDS=12 – reine Tool-Folge ohne finale Antwort bricht ab.
Läuft ein Slave über 12 aufeinanderfolgende Runden, deren einzige Ausgabe
Tool-Calls sind (ohne je zu content zu kommen), wird abgebrochen
(„Tool-Only-Dauerschleife"). Verschiedene, legitime Tool-Sequenzen in Folge
sind bis 12 Runden erlaubt; reasoning-/content-Runden resetteten die Zähler
(stalled_rounds, prev_tool_signature, tool_only_rounds), damit
Nicht-Tool-Runden die Direkt-Folge unterbrechen.
Alle drei Änderungen sind durch isolierte Loop-Simulationen verifiziert
(identische Wiederholung → Abbruch nach 4 Requests; Tool-Only-Folge → Abbruch
nach 12; ein Tool-Call + finale Antwort → normaler Erfolg in 2 Runden).
Beeinflusste Datei: nur modules/orchestrator.py → reload_modules genügt,
kein Gateway-Neustart, keine Client-Neuverbindung nötig.
Stand 2026-08-29: Härtung (alle Feinschliffe live in gateway.py/orchestrator.py/wiki.py)
Neue Garantien, umgesetzt nach den Fehlschlägen des Session-Tages — jede
Meldung hier ist durch einen Testlauf belegt:
Concurrency = 1 (hart)._CALL_LOCK in gateway.py lässt nur EINEN
gleichzeitigen Tool-Call zu (über alle Tools/Sessions hinweg). Ein zweiter
eingehender Call wird sofort abgelehnt („Gateway beschaeftigt …”),
es wird nichts gestapelt. Verifiziert: zwei parallele Calls → genau einer
läuft, der zweite wird abgelehnt.
Automatischer Abbruch bei weggefallenem Client. Die SSE-Schleife setzt
bei Verbindungsende set_cancel(True); der Orchestrator prüft zwischen den
Tool-Runden auf cancel_requested() und stoppt inklusive hartem
Gesamtlimit. Kein Weiterlaufen bis Runde 100 mehr, wenn der Auftraggeber
schon weg ist.
reload_modules überlebt Reloads. Die Registrierung liegt jetzt IN
load_modules() (vorher nur einmal beim Start → Selbstzerstörung nach dem
ersten Reload). Verifiziert: zwei aufeinanderfolgende Reloads, Tool bleibt
da. Konsequenz: Änderungen an modules/*.py brauchen keinen Neustart
mehr; nur gateway.py selbst.
wiki_lesen liefert Artikel vollständig. Die 8000-Zeichen-Kappung ist
entfernt — zu kleine Limits zerstören den Sinn des Artikels. Verifiziert:
dieser Artikel kommt komplett (≈16 000 Zeichen) ohne Kürzungshinweis.
Delegation ist nachvollziehbar. Der Orchestrator loggt jede Delegation
mit Start/Ende in journalctl:
[delegat] START slave_type=… tools=[…] task=… Zeichen und
[delegat] ENDE status=OK/FEHLER dauer=…s rounds=… antwort=… Zeichen
sowie pro Runde [delegat] Runde N: Tool '…' -> … Zeichen. Damit ist ein
-32001-Ausgang (Client-Wattfenster) nachträglich diagnizierbar.
Großzügige Zeitgrenzen.MAX_READ_SECONDS = 3600, MAX_DELEGATE_SECONDS = 3600 (vorher 900/1500). Bei ~3,5 Tokens/s ist 900 s schon bei einer
mittleren write-Aufgabe unterschritten — ein zu kleines Fenster kappte
den Call mitten in der Generierung, obwohl der Slave die Token bereits
erzeugt hatte. Disziplin bleibt trotzdem: kleine Slices (~1–3 Tool-Calls),
ein Element der Kernregeln.
Praxis-Regel aus dem Vorfall: Ein großer Aufgabe (z. B. komplette Datei
schreiben, >~500 Generierungs-Token) kann dennoch an der Gesamtdauer scheitern
und verbrennt die Slaven-Token ohne Ergebnis. Für zuverlässige
Delegations-Ergebnisse gilt weiter: Aufgaben in kleine, einzeln verifizierbare
Scheiben teilen und einen Call pro Scheibe machen.
Inhalt
Systemaufbau
Master (Agent in der Cloud / teuer)
│ delegiert (MCP)
▼
MCP-Gateway :3000/sse (mcps/gateway.py, lädt modules/*.py)
│ lädt Module / hält Tool-Handler
▼
orchestrator.py (Registry, Tool-Sets, Tool-Loop)
│ adressiert den Slave über seine Modell-ID
▼
LM Studio :1234/v1/chat/completions (Modell: meta/muse-glimmer 28B)
Gateway-Prozess:mcps/gateway.py, läuft als systemd-User-Service
(mcp-gateway.service). Lädt modules/*.pyeinmal beim Start – Änderungen
an Modulen erfordern deshalb einen Neustart des Services.
Registry:AVAILABLE_SLAVES mappt Slave-Typen (coder, math, general)
auf Modell-IDs – aktuell alle drei auf Muse Glimmer 28B.
Adressierung erfolgt stets über die Modell-ID (model_id) im Request.
Modell-Ladung: Glimmer mit -c 16384 --gpu 0.85 (teils CPU-Offload).
Parameter, Kontext, GPU-Anteil beeinflussen die Zuverlässigkeit.
Erstinstallation & Schnellstart
Voraussetzungen: Python 3.10+, ausreichend VRAM/RAM für ein 28B-Modell,
LM Studio mit aktivierter OpenAI-kompatibler Server-API auf
127.0.0.1:1234 und geladenem meta/muse-glimmer.
# 1. Quellen liegen unter ~/Dokumente/daten/sandbox/aux/mcpscd ~/Dokumente/daten/sandbox/aux/mcps# 2. Python-Umgebungpython3 -m venv venvsource venv/bin/activatepip install -r requirements.txt# (Alternativ läuft das Komplett-Setup inkl. Systemdeps über ./setup.sh# bzw. der Paket-Nachinstall über ./install_python.sh.)
# 4. Health-Checkcurl -s --max-time 3 http://127.0.0.1:3000/sse # → SSE-Endpoint-Feldcurl -s http://127.0.0.1:1234/v1/models # → meta/muse-glimmer muss gelistet seinjournalctl --user -u mcp-gateway.service --since "1 min ago" # „Slave-Tools geladen: N …"
# 5. Client (z. B. opencode) anbinden# ~/.config/opencode/opencode.jsonc → mcp.gateway (s. „Registrierung in opencode")
Komplett-Schnelltest (Ende-zu-Ende):
call_tool("delegate_to_slave", { "slave_type": "general", "task": "Antworte nur mit OK.", # 1: Smoke-Test der Kette "max_tokens": 256,})call_tool("delegate_to_slave", { "slave_type": "coder", "task": "Liste Ordner X auf und zeige Datei Y", # 2: Lese-Test "tool_names": ["ls", "cat"],})call_tool("delegate_to_slave", { "slave_type": "coder", "task": "Schreibe Datei X mit Inhalt Y", # 3: Schreib-Test "tool_names": ["write"],})
Bei jedem Schritt der Verifikation: Timing (siehe „Performance-Rahmenbindung”),
nicht parallel delegieren, Beschreibung + Resultat prüfen.
Hinweis zum Testcode oben: max_tokens: 256 ist NUR für die Minimal-Antworten
des Smoke-Tests („OK”) gedacht. Für echte Aufgaben den Standard 8192 verwenden
und nicht künstlich senken (s. Kernregel 3).
Werkzeug-Auswahl für den Slave (wichtigste Erkenntnis)
Glimmer verhält sich beim Tool-Picking sehr empfindlich. Gemessene Regeln:
Kleine Tool-Sets sind zuverlässig, große nicht. Mit >13 Tools degradiert
das Tool-Picking; schon bei 5 Datei-Tools (write, edit, ls, cat,
grep) blieb der Tool-Call aus (leerer Content, keine tool_calls). Mit
einem einzigen Tool ruft Glimmer es zuverlässig auf.
Konsequenz: Relevanz vor Vollständigkeit. Nur Tools anbieten, die zur
konkreten Aufgabe passen. Seit 2026-08-29 sind für die Coder-Slaves Lese- und
Schreib-Werkzeuge freigeschaltet (s. u.), damit der Slave selbstständig lesen
und schreiben kann.
Daraus entstand die aufgabengebundene Tool-Freigabe: delegate_to_slave
akzeptiert optional tool_names (Liste). Nur diese Werkzeuge sind für den
Aufruf sichtbar; ohne Angabe gilt das Standard-Set des Slave-Typs.
# Beispiel: Slave soll einen Ordner lesen — er sieht genau `ls` und `cat`call_tool("delegate_to_slave", { "slave_type": "coder", "task": "Liste Ordner X auf und zeige Datei Y", "tool_names": ["ls", "cat"],})
Aktuelle Tool-Sets (Stand 2026-08-29)
Konfiguration in modules/orchestrator.py, SLAVE_TOOLS_BY_TYPE:
coder:ls, cat, grep, filesystem_info, write, edit,
rechnen, statistik
math: nur Rechen-/Konverter-Tools (rechnen, statistik,
integriere, differenziere, alle Einheiten-Konverter)
Hintergrund: Solange coder/general nur write durften, konnte der Slave
Projekt-Header nicht lesen und musste alle nötigen Infos eingebettet bekommen.
Mit ls/cat/grep kann er selbst nachschlagen; edit erlaubt gezielte
Ersetzungen statt Ganzdatei-Writes.
Der Reasoning-feste Tool-Loop
Zweite zentrale Erkenntnis: reasoning_content darf den Tool-Loop nicht
beenden. Das Modell „denkt” (Reasoning) nach, bevor es den Tool-Call eines
Drafts emittiert. Wurde dieses Reasoning fälschlich als finale Antwort gewertet,
brach der Loop mit „0 Werkzeug-Aufrufe” ab, obwohl das Modell noch arbeiten
wollte.
Der Fixed Loop (orchestrator.py, _chat_with_tools):
tool_calls vorhanden → ausführen, Ergebnis in den Verlauf legen, weiter.
content vorhanden (echte finale Textantwort) → Loop beenden.
nur reasoning_content, kein Content/Tool-Call → als Zwischenschritt
weitertragen (Verlauf assistant + reasoning_content) und weiter
iterieren; bei zu vielen aufeinanderfolgenden Reasoning-Schritten
(MAX_REASONING_ROUNDS = 6) abbrechen.
leere Antwort → Warnung zurückgeben.
Grenzwerte: MAX_TOOL_ROUNDS = 100, Request-Timeout 900 s
(MAX_REASONING_ROUNDS = 6).
Anwendung (Praxis-Muster)
Für Implementierungsarbeit im erlaubten Pfadrahmen:
Aufgabe in kleine, einzeln verifizierbare Schritte teilen – ein
synchroner MCP-Call soll nur ~1–3 Tool-Calls ausführen, damit er schnell
durchläuft (keine 15-min-Calls).
tool_names auf genau die nötigen Werkzeuge beschränken (z. B. ls+cat
zum Nachlesen, nur write für eine Datei, edit für ein gezieltes Ersetzen).
Der Slave kann seit 2026-08-29 selbst ls/cat/grep nutzen – präzise
Pfade und Dateinamen in die Task schreiben, damit er gezielt nachschlägt.
Vollständige Information (z. B. wörtlicher Header/API-Namen) einbetten, wenn
er ohne Lese-Zugriff reine write-Aufgaben ausführen soll – sonst rät er
Bezeichner.
Der Master verifiziert das Ergebnis und korrigiert gezielt, dann committet
er (Committen bleibt Master-Aufgabe).
max_tokens/timeoutgroßzügig (Standard 8192); ein Call, der wegen zu
knapper Zuteilung abbricht, kostet mehr als ein sauberer zweiter.
Zuverlässige Delegation: Datei schreiben (write allein) → Slave erstellt
die Datei selbst (im Test 2 Tool-Calls).
Grenze: Große, mehrschrittige Implementierungen überfordern einen einzelnen
synchronen MCP-Call; lieber slice-weise delegieren.
Betrieb
Gateway als systemd-User-Service (mcp-gateway.service):
systemctl --user status mcp-gateway.service # läuft? letzte Requestssystemctl --user restart mcp-gateway.service # nach Modul-/Konfig-Änderungsystemctl --user stop mcp-gateway.servicesystemctl --user start mcp-gateway.servicejournalctl --user -u mcp-gateway.service -f # Live-Logjournalctl --user -u mcp-gateway.service --since "5 min ago"
Nur bei tatsächlichem Hänger (Tracebacks, kein SSE-Endpoint, LM Studio ohne
Modell) systemctl --user restart mcp-gateway.service ausführen und danach den
Client neu verbinden. Fehler bei einzelnen delegate_to_slave-Calls sind
kein Grund für einen Restart — die Ursache liegt dort fast immer in Task oder
Tool-Set (s. Troubleshooting).
Wichtig: Das Gateway ist zustandsbehaftet gegenüber Clients – nach einem
Neustart nutzen bestehende MCP-Clients (z. B. opencode) die alte
SSE-Session-ID weiter und erhalten -32602. Immer neu verbinden (s. Troubleshooting).
Troubleshooting
Symptom
Ursache
Behebung
MCP error -32602: Invalid request parameters auf ALLEN Gateway-Tools
Gateway neu gestartet, opencode hält abgelaufene SSE-Session
MCP-Server in opencode neu verbinden (TUI /mcp-Menü: gateway togglen) oder opencode neu starten
MCP error -32001: Request timed out
Synchroner Call läuft länger als der Client wartet (LM Studio langsam, viele Tool-Runden)
Abgeschnittenes Ergebnis / „unvollständig” ohne Fehler
max_tokens künstlich niedrig gesetzt
Keine künstlichen Limits: Standard 8192 verwenden, für größere Tasks höher (s. Kernregeln)
Slave/Gateway wirkt „kaputt”
Häufig KEIN Gateway-Defekt
Erst diagnostizieren (s. „Diagnose vor Gateway-Neustart”); Restart nur bei nachgewiesenem Hänger
„❌ ADRESSIERUNG FEHLGESCHLAGEN: Slave ist NICHT in LM Studio geladen”
Modell meta/muse-glimmer nicht geladen
In LM Studio das Glimmer-Modell laden, Aufruf wiederholen
„❌ tool_names […] enthält keine verfügbaren Werkzeuge für ‘coder‘“
Tool-Name nicht im Set des Slave-Typs (SLAVE_TOOLS_BY_TYPE)
Gültige Namen des Typs verwenden bzw. erst freischalten (s. u.)
Slave antwortet, aber 0 Werkzeug-Aufrufe obwohl gewollt
Reasoning-Only-Loop oder Tool-Set zu groß
Task mit exakt passender tool_names-Liste wiederholen; MAX_REASONING_ROUNDS=6 begrenzt Endlosschleifen
Slave läuft endlos und emittiert immer dieselben Tool-Calls / nie finale Antwort (Stundenlauf)
Tool-Loop-Stall (glummer-Wiederholungs-Schleife)
Seit 2026-08-30 automatisch durch MAX_STALLED_ROUNDS/MAX_TOOL_ONLY_ROUNDS abgebrochen; für Wiki-Wissen zusätzlich wiki_* im general-Set
Slave „denkt” extrem lange (Stunden/Minuten)
CPU-Offload: Glimmer generiert nur ~3,5 Tokens/s
Tasks klein halten; ~500 Tokens ≈ 2–3 min einplanen; nicht parallel delegieren (teilen GPU-Slots)
Tool-Freischaltung (Änderung an einem Slave-Tool-Set)
modules/orchestrator.py → SLAVE_TOOLS_BY_TYPE editen (Tool-Namen aus den
Tool-Definitionen der Modul-Dateien, z. B. filesystem.py). Nur Namen
verwenden, die existieren, sonst schlägt der Filter fehl.
Mehrere parallel gestartete Delegates laufen gleichzeitig gegen dieselben
GPU-Slots und verlangsamen sich gegenseitig – nicht parallel delegieren.
Ein delegate_to_slave-Call mit mehreren Werkzeugen kann den
900-s-Request-Timeout erreichen; lieber aufteilen.
Keine künstlichen Limits: max_tokens großzügig (Standard 8192) wählen.
Ein knappes Budget bricht die Generierung ab — bei 3,5 T/s ist das teurer
(Neu-Start) als ein größer geplanter, sauberer Aufruf.
write/edit sind strikt auf erlaubte Pfade begrenzt (positiv/negativ getestet);
Systemmodifikation, Compiler-Zugriff und TTS sind über die SAFE_SLAVE_MODULES-
Whitelist geregelt.
Der Slave wird nur über delegate_to_slave angesprochen; tool_names begrenzt
das Tool-Angebot pro Aufgabe zusätzlich.
Netzwerk: Gateway bindet nur 127.0.0.1:3000 (gateway.py), LM Studio
127.0.0.1:1234 – das System lauscht bewusst nur lokal (kein Remote-Zugriff,
keine Credentials im Netz).
Zusätzlich liegt …/mcps/config.json mit dem SSE-URL der Gateway-Beschreibung
vor. Nach einem Gateway-Neustart muss der Client neu verbinden (s. Troubleshooting).