Files
mission-control-v2/mcp/mcp_memory.py
T
Hitonabi 81468df9c0 feat(2.0): Phase 3 — Memory + MCP
Geteiltes Gedaechtnis: services/memory.py (SQLite/WAL, 5 Kategorien,
Dedupe-Kurator deterministisch), routers/memory.py (CRUD/export/dedupe).
MCP: mcp/mcp_memory.py (Guard-Beschreibungen gegen 14B-Loop) + mcp/mcp_mc.py
NEU (Stack-Management fuer Hermes: list/discover/register/route/restart/
status). Frontend MemoryView (Add/Filter/Suche/Delete/Aufraeumen).

Lokal verifiziert: CRUD + Dedupe (TestClient), MCP-Server syntax-OK,
Frontend-Build + Browser (MemoryView). Docs aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 08:16:12 +02:00

109 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""
Mission Control 2.0 — Memory MCP Server (geteiltes Gedächtnis für ALLE Tools).
Stdio-MCP-Server. Läuft als Subprocess von Cline/Claude Code/OpenCode/Hermes.
Ruft die /api/memory-Endpunkte von Mission Control via HTTP auf.
Tool-Beschreibungen bewusst als GUARDS formuliert (konditional, nicht imperativ),
damit kleine Modelle (z.B. Hermes 14B) nicht in Aufruf-Schleifen laufen.
Env: MC_URL (default http://192.168.178.151:9000), MC_TOKEN (optional).
Install (auf dem Rechner des Tools): pip install mcp httpx
"""
import os
import httpx
from mcp.server.fastmcp import FastMCP
MC_URL = os.environ.get("MC_URL", "http://192.168.178.151:9000").rstrip("/")
MC_TOKEN = os.environ.get("MC_TOKEN", "")
mcp = FastMCP("mission-control-memory")
def _h() -> dict:
return {"X-MC-Token": MC_TOKEN} if MC_TOKEN else {}
def _get(path: str, **params):
r = httpx.get(f"{MC_URL}{path}", headers=_h(), params=params, timeout=10)
r.raise_for_status()
return r.json()
def _post(path: str, data: dict):
r = httpx.post(f"{MC_URL}{path}", headers=_h(), json=data, timeout=10)
r.raise_for_status()
return r.json()
def _put(path: str, data: dict):
r = httpx.put(f"{MC_URL}{path}", headers=_h(), json=data, timeout=10)
r.raise_for_status()
return r.json()
def _delete(path: str):
r = httpx.delete(f"{MC_URL}{path}", headers=_h(), timeout=10)
r.raise_for_status()
return r.json()
@mcp.tool()
def get_memories(category: str = "") -> str:
"""Lädt gespeicherte Fakten/Entscheidungen aus dem geteilten Gedächtnis.
Nutze dies EINMAL am Session-Beginn, wenn du Projekt-Kontext brauchst — nicht wiederholt.
category: user | instruction | stable | versioned | ephemeral | (leer = alle)"""
items = _get("/api/memory", **({"category": category} if category else {}))
if not items:
return "Keine Memories gespeichert."
icon = {"stable": "🔵", "versioned": "🟡", "ephemeral": "", "user": "👤", "instruction": "📋"}
return "\n".join(
f"{icon.get(m['category'], '·')} [{m['category']}] {m['content']} (ID: {m['id'][:8]})"
for m in items
)
@mcp.tool()
def search_memories(q: str) -> str:
"""Sucht per Stichwort in den Fakten. Nutze dies VOR add_memory (Dubletten-Check)
oder wenn du eine konkrete frühere Entscheidung suchst."""
items = _get("/api/memory", q=q)
if not items:
return f"Keine Treffer für '{q}'."
return "\n".join(f"[{m['category']}] {m['content']} (ID: {m['id'][:8]})" for m in items)
@mcp.tool()
def add_memory(content: str, category: str = "stable", source: str = "agent") -> str:
"""Speichert EINEN dauerhaften Fakt im geteilten Gedächtnis (alle Tools sehen ihn).
Nur aufrufen, WENN gerade etwas Dauerhaftes entstanden ist (Konvention, Architektur-
Entscheidung, Tech-Version, Nutzer-Präferenz) UND es noch nicht existiert (vorher
search_memories!). Knapp & atomar. Existiert ein passender Eintrag → update_memory.
category: user | instruction | stable | versioned | ephemeral"""
m = _post("/api/memory", {"content": content, "category": category, "source": source})
return f"Gespeichert (ID: {m['id'][:8]}): {content}"
@mcp.tool()
def update_memory(memory_id: str, content: str = "", category: str = "") -> str:
"""Aktualisiert einen bestehenden Eintrag (nur gesetzte Felder)."""
data = {k: v for k, v in (("content", content), ("category", category)) if v}
if not data:
return "Nichts zu aktualisieren."
m = _put(f"/api/memory/{memory_id}", data)
return f"Aktualisiert: {m['content']}"
@mcp.tool()
def delete_memory(memory_id: str) -> str:
"""Löscht einen veralteten/falschen Eintrag anhand seiner ID."""
_delete(f"/api/memory/{memory_id}")
return f"Eintrag {memory_id[:8]} gelöscht."
if __name__ == "__main__":
mcp.run()