Files
mission-control-v2/mcp/mcp_voice.py
T
HitonabiandClaude Fable 5.1 479fecd2c6
Ampel / ampel (push) Failing after 24s
Auftragsbuch ausgebaut: Karten-Annahme, Bagatell-Annahme, Lucy-Annahme raus — Gate ist Ampel + User-Merge
Die Vorschlags-Inbox (routers/services auftragsbuch, AuftragsbuchView, /auftraege, Cockpit-
Kachel + Handlungsbedarf-Eintrag, deploy/auftrag-annehmen.sh, lucy-annahme.sh, bagatell-
annahme.sh, docs/AUFTRAGSBUCH.md) war seit 02.08.2026 ungenutzt: Kanban-Tools seit 21.08. aus,
v3-Umbau lief ueber Branch -> Ampel -> Merge -> deploy.sh, PC-Executor-IP in Box und Config
veraltet. Doku behauptete den Karten-Weg trotzdem als Standard.

Jetzt:
- Cockpit: Kachel "Ideen" (offen/haengend) statt "Auftragsbuch"; Handlungsbedarf zeigt haengende
  Ideen und springt in die Ideen-Ansicht.
- Guide/Schaubild: Kreislauf Idee -> Ideen-Queue -> IDE am PC -> Ampel -> DEIN Merge -> Live.
- events.py: kein Auftragsbuch-Fingerprint mehr; config.py: PC_EXECUTOR_URL-Default auf .22.
- Skills/Skripte: selbst-inventur ohne Karten-Abschnitt, morning-report nennt offene Branches,
  konzept-fliessband verweist auf die Ideen-Ansicht.
- Doku: STACK "Deploy & Pipeline" = Branch -> Ampel -> User-Merge -> deploy.sh (einziger Weg),
  GRENZEN/ARBEITSWEISE/FALLEN/README/BEDIENUNG/RUNBOOK/GEMINI_BRIEFING/gitea-workflow angepasst,
  ZIELBILD Punkt 9, OFFENE-FAEDEN + RAPHAEL.md: erledigt.
- Bewusst geblieben: werkstatt-SOUL/projektstart-SOUL (Werkstatt-Persona, eigener Faden),
  Chronik-Kategorie "auftragsbuch" fuer historische Eintraege.
Auf der Box bereits erledigt (Hand): mc2-bagatell.timer deaktiviert + Units archiviert,
PC-Executor-URL in ~/.hermes/config.yaml auf .22.

Baut auf wartung/lucy-stimme-proxy-raphael auf (Proxy /api/lucy/stimme/*).
Gates: eslint 0 Fehler, vitest 59/59, tsc + vite build gruen, frontend/dist committet,
py_compile gruen.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 16:25:05 +02:00

121 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Mission Control 2.0 — schlanker Voice-MCP-Server (für Lucys Sprach-Lane).
Prompt-Diät (Faden 7): die api_server-Plattform (Lucy Desktop) bekommt per
platform_toolsets.api_server NUR diesen dünnen Server statt der vollen
mission-control-stack-Control-Plane (26 Tools, ~große Schemas). Enthalten ist,
was man einer gesprochenen Assistentin zuruft: Erinnerungen + „geht's der Box gut?".
CLI/Telegram/Werkstatt behalten den vollen Stack (mcp_mc.py) — hier NICHTS ergänzen,
was nicht in ein Sprachgespräch gehört.
Env: MC_URL (default http://127.0.0.1:9001).
"""
import os
import httpx
from mcp.server.fastmcp import FastMCP
MC_URL = os.environ.get("MC_URL", "http://127.0.0.1:9001").rstrip("/")
mcp = FastMCP("mission-control-voice")
def _get(path: str, **params):
r = httpx.get(f"{MC_URL}{path}", params=params, timeout=30)
r.raise_for_status()
return r.json()
def _post(path: str, data: dict):
r = httpx.post(f"{MC_URL}{path}", json=data, timeout=30)
r.raise_for_status()
return r.json()
@mcp.tool()
def reminder_create(text: str, when: str, repeat: str = "") -> str:
"""Legt eine Erinnerung an: zur angegebenen Zeit sagt die Box dem Commander den Text AKTIV
an (Lucy spricht + Telegram). NUR nutzen, wenn der Commander ausdrücklich erinnert werden
will ('erinner mich', 'sag mir um', 'jeden Morgen').
when = ISO-8601 mit Datum UND Uhrzeit in der Lokalzeit des Commanders, z.B. 2026-07-04T09:00.
Relative Angaben ('in 20 Minuten', 'morgen früh') selbst aus der aktuellen Zeit im Kontext
umrechnen. repeat: '' einmalig | daily (täglich) | weekdays (MoFr) | weekly (wöchentlich).
text = die Ansage selbst, kurz und direkt (ohne 'Erinnerung:' davor — das ergänzt die Box)."""
try:
item = _post("/api/reminders", {"text": text, "when": when, "repeat": repeat})["item"]
except httpx.HTTPStatusError as e:
return f"Fehlgeschlagen: {e.response.json().get('detail', e.response.text[:200])}"
rep = {"daily": " (täglich)", "weekdays": " (werktags)", "weekly": " (wöchentlich)"}.get(item["repeat"], "")
return f"Erinnerung #{item['id']} angelegt: {item['when_local']}{rep} — „{item['text']}“"
@mcp.tool()
def reminder_list() -> str:
"""Zeigt alle anstehenden Erinnerungen/Routinen (Nummer, Zeit, Text). Vor dem Löschen
hiermit die Nummer ermitteln."""
items = _get("/api/reminders")["items"]
if not items:
return "Keine Erinnerungen angelegt."
rep = {"daily": " · täglich", "weekdays": " · werktags", "weekly": " · wöchentlich"}
return "\n".join(f"#{i['id']} · {i['when_local']}{rep.get(i['repeat'], '')}{i['text']}" for i in items)
@mcp.tool()
def reminder_delete(reminder_id: int) -> str:
"""Löscht eine Erinnerung/Routine endgültig (Nummer aus reminder_list). NUR auf
ausdrücklichen Wunsch des Commanders."""
try:
r = httpx.delete(f"{MC_URL}/api/reminders/{reminder_id}", timeout=30)
r.raise_for_status()
gone = r.json()["deleted"]
except httpx.HTTPStatusError as e:
return f"Fehlgeschlagen: {e.response.json().get('detail', e.response.text[:200])}"
return f"Erinnerung #{gone['id']} gelöscht ({gone['when_local']} — „{gone['text']}“)."
@mcp.tool()
def box_status() -> str:
"""Kurzer Gesundheits-Check der Box (KI-Server): Engine, Agent-Hirn, Kern-Dienste.
Nutze dies, wenn der Commander fragt, ob mit der Box/dem System alles in Ordnung ist."""
try:
h = _get("/api/health")
except Exception as exc:
return f"MC2 antwortet nicht ({exc}) — das Backend selbst könnte down sein."
brain = h.get("brain") or {}
lines = [
f"{'✅' if h.get('engine_reachable') else '❌'} Modell-Engine",
f"{'✅' if brain.get('ready') else '❌'} Agent-Hirn ({brain.get('model', '?')})",
]
try:
svcs = _get("/api/system/services").get("services", [])
lines += [f"{'✅' if s.get('ok') else '❌'} {s.get('name')}" for s in svcs]
except Exception:
lines.append("(Dienste-Liste nicht abrufbar)")
return "\n".join(lines)
@mcp.tool()
def idee_notieren(titel: str, notiz: str = "") -> str:
"""Legt eine Idee/einen Auftrag des Commanders in die Ideen-Queue der Box. Nutzen, sobald
der Commander eine Idee für später äußert ('wäre cool wenn …', 'bau mal irgendwann …',
'die Box soll …') — NICHT nur versprechen. Die Box arbeitet die Idee selbst aus und baut;
das Ergebnis landet als Branch auf Gitea; gemergt wird nur vom Commander (Ampel gruen).
titel = kurze deutsche Überschrift; notiz = seine Worte plus nötiger Kontext."""
try:
r = _post("/api/ideen", {"titel": titel, "notiz": notiz})
except httpx.HTTPStatusError as e:
try:
detail = e.response.json().get("detail", "")
except Exception:
detail = e.response.text[:200]
return f"Fehlgeschlagen: {detail}"
except Exception as exc:
return f"Queue nicht erreichbar ({exc})."
return f"Idee ist in der Queue ({r.get('id', '?')}). Die Box arbeitet sie von allein ab."
if __name__ == "__main__":
mcp.run()