MCP-Server ki-web
Dies ist ein Model Context Protocol-Server, der den drei lokal installierten AI-CLIs (Claude Code, Codex, qwen-code) Werkzeuge zum Verwalten von Seiten auf ki.karlkratz.com bereitstellt. Wenn der KI z. B. mitgeteilt wird „lege eine Seite an", ruft sie das Tool create_page auf, der Server schreibt in die Datenbank, die Seite ist sofort live.
Datenfluss
- KI-CLI erkennt aus dem Prompt, dass ein Tool gebraucht wird.
- Sie startet den MCP-Server ki-web als stdio-Subprozess und sendet JSON-RPC.
- Die Python-Schicht ruft die PHP-JSON-API unter /api/pages auf.
- PHP validiert (Slug-Regex, Längen-Limits), sanitisiert das HTML und schreibt nach Postgres (Datenbank ki_web).
- Die Antwort läuft denselben Pfad zurück bis zur KI.
Tools
| Tool | Wirkung |
|---|---|
| list_pages | Alle Seiten, mit Suche, Sortierung, Pagination |
| get_page | Eine Seite per Slug |
| create_page | Neue Seite anlegen |
| update_page | Felder einer bestehenden Seite überschreiben |
| delete_page | Seite per Slug löschen |
Architektur, DDD / Hexagonal
Beide Codebases, die PHP-App und der Python-MCP, folgen demselben Muster, jede Datei unter 100 Zeilen.
PHP unter /var/www/ki.karlkratz.com/src/
- Domain: Page-Entity, Value Objects (Slug, Title, MetaDescription, Content), Repository-Port, HtmlSanitizer-Port, Exceptions.
- Application: Use Cases CreatePage, UpdatePage, DeletePage, GetPageBySlug, ListPages, dünne Orchestrierung.
- Infrastructure: PdoPageRepository, DomHtmlSanitizer, Router, Controllers, Views, alles Frameworkartige bleibt hier außen.
Python unter /var/www/mcp/
- domain/page.py: Dataclass und PagesError.
- application/use_cases.py: PageUseCases, dünner Wrapper.
- infrastructure/pages_client.py: httpx-Client gegen die PHP-API.
- server.py: stdio-Entry, registriert die fünf Tools beim MCP-SDK.
DRY: wo passiert Validierung?
Ausschließlich in der PHP-Schicht. Der Python-MCP ist ein dünner HTTP-Adapter. Slug-Regex, Längen-Limits und die HTML-Tag-Whitelist (h1, h2, h3, p, ol, ul, li, a, strong, table, pre, code) leben ausschließlich im PHP-Code. Das Python-Modul kennt diese Regeln nicht und muss sie auch nicht kennen, fügt man später eine zweite Schnittstelle hinzu (CLI-Tool, Webhook), bleibt die Regel weiterhin an genau einer Stelle.
HTML-Sanitization im Detail
Der DomHtmlSanitizer in PHP parst den eingehenden Body mit DOMDocument, läuft den Baum rückwärts ab und entfernt jeden Tag, der nicht auf der Whitelist steht. Das a-Tag darf nur die Attribute href und title behalten; href muss http(s), mailto oder ein relativer Pfad sein. Damit kann auch eine halluzinierende KI keinen Schaden anrichten, ein eingeschleustes Script-Tag wird vor dem INSERT entfernt.
Selbst ausprobieren
- In Claude Code, Codex oder qwen-code formulieren: „lege eine Seite mit Slug foo, Titel ‚Foo‘, kurzer Meta-Description und H1+Absatz an".
- Die CLI ruft create_page auf.
- Die Seite ist sofort unter ki.karlkratz.com/foo erreichbar.
- Oder direkt suchen in /admin.
Wichtigste Pfade
| Pfad | Inhalt |
|---|---|
| /var/www/mcp/ | Python-MCP-Quellen |
| /var/www/ki.karlkratz.com/src/ | PHP-Codebase |
| /var/www/ki.karlkratz.com/public/index.php | Front-Controller |
| /var/www/_credentials/ki_web/credentials.json | DB-Zugang für PHP-App |
| /usr/local/bin/ki-web | Wrapper, startet den MCP über venv-Python |