Files
WP-LIT-MCP/docs/superpowers/specs/2026-09-25-wp-lit-mcp-design.md
T
2026-09-27 03:33:38 +02:00

502 lines
39 KiB
Markdown
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.
# WP-LIT-MCP – Design-Spezifikation
- **Datum:** 2026-09-25
- **Status:** Entwurf zur Freigabe
- **Autor:** Jan-Niclas Loosen (Loosen-IT), erarbeitet mit Claude
## 1. Ziel und Kontext
### 1.1 Problem
Die Pflege von Kunden-Websites (WordPress + Divi 5 + Pods) kostet viel Handarbeit: Seiten anlegen, in Menüs hängen, Divi-Abschnitte bauen, Corporate Designs in Divi übertragen, Custom-Post-Type-Einträge befüllen. Bestehende WordPress-MCP-Plugins erzeugen nur rohes HTML, das im Divi-Builder nicht weiterbearbeitet werden kann. Der Divi AI Agent (Divi 5) arbeitet nur im Builder, läuft über das kostenpflichtige AI Wallet und bietet (Stand 2026-09) keine MCP-Schnittstelle.
### 1.2 Ziel
Ein WordPress-Plugin, das auf jeder Kundenseite einen MCP-Server bereitstellt, mit dem Claude (claude.ai-Connector) die Website pflegt – **so, dass alle Ergebnisse native Divi-Strukturen sind, die der Mensch im Divi Visual Builder weiter verfeinern kann.**
### 1.3 Erfolgskriterien
1. Ein claude.ai-Connector verbindet sich per OAuth mit einer Kundenseite; nur freigegebene Rollen können Tools nutzen.
2. Von Claude erzeugte oder geänderte Divi-Inhalte sind im Visual Builder vollständig bearbeitbar (manuell verifiziert pro Etappe).
3. Unangetastete Inhalte bleiben byte-identisch erhalten. Dokumentierte Ausnahme (Ruling S7): Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (`AttributeSecurity`/`HtmlSecurity` auf `wp_insert_post_data`, laufen für jeden Benutzer und werden nie entfernt) – aber nur, wenn die Seite noch nicht in Divis `serialize_blocks()`-Form gespeichert war (z. B. Import, direkter DB-Schreibzugriff); über Builder oder LIT MCP gespeicherte Seiten bleiben unverändert (E2E Abschnitt 8a); die Antwort meldet das als Warnung „Inhalt beim Speichern verändert“ (`CHANGED_ON_SAVE`), die Seite bleibt gültig (E2E `divi-write-smoke.sh` Abschnitt 8).
4. Eine neue Website (Seitenstruktur, Menü, Einstellungen, Designsystem aus einem CD) lässt sich im Dialog aufsetzen.
5. Jede Änderung ist nachvollziehbar (Protokoll) und umkehrbar (Revision bzw. Snapshot).
### 1.4 Verifizierte Grundlagen
- Handgeschriebenes Divi-5-Block-Markup (`<!-- wp:divi/section … -->`) wird von Divi als native Module gerendert (`et_pb_section`, `et_pb_heading`, …) und ist im Visual Builder bearbeitbar (lokal getestet, 2026-09-25).
- Divi liefert serverseitig alle Modul-Metadaten: `ModuleRegistration::get_all_core_modules_metadata()` (100 Module mit Attribut-Schemata), Default-Attribute über `ModuleRegistration::get_default_attrs()`.
- WordPress 7.0 enthält die Abilities API im Kern.
## 2. Grundprinzipien
1. **Divi-nativ:** Es werden nur echte Divi-Blöcke erzeugt. Das Code-Modul ist erlaubt, wird aber nachrangig behandelt (Katalog-Hinweis „nur wenn kein natives Modul passt“, Warnung bei Verwendung). Jedes erzeugte Element erhält ein sprechendes Admin-Label.
2. **Designsystem an der Quelle:** Farben, Schriften, Variablen, Presets werden immer live aus Divi/WordPress gelesen; es gibt keine doppelte Pflege. Neue Inhalte nutzen Variablen und Presets statt fester Werte.
3. **Generisch vor fest verdrahtet:** Post-Typen, Taxonomien und Felder werden pro Installation erkannt. Spezifische Logik nur über generische Erweiterungspunkte.
4. **Integrationen nach Verfügbarkeit:** Jede Integration (Divi, Pods, …) registriert ihre Tools nur, wenn das Theme/Plugin aktiv ist.
5. **Status bleibt erhalten:** Schreib-Tools ändern nie den Veröffentlichungsstatus. Veröffentlichen/Zurückziehen/Planen sind eigene, explizite Tools. Veröffentlichte Inhalte werden live bearbeitet (mit Revision).
6. **Produktiv tauglich:** Jeder Aufruf läuft mit den Rechten des verbundenen WP-Benutzers; zusätzlich Rollen-Freigabe und Rechteprüfung pro Tool.
7. **Nichts geht verloren:** Revision oder Snapshot vor jeder Änderung, optimistische Sperre (`rev`) gegen das Überschreiben paralleler Handarbeit.
## 3. Umfang
### 3.1 v1 – Muss
| # | Bereich | Inhalt |
|---|---|---|
| 1 | Anbindung | OAuth 2.1 für claude.ai (CIMD), Rollen-Gate, Verbindungsübersicht mit Widerruf |
| 2 | Content generisch | Schema-Erkennung (Post-Typen, Taxonomien, WP-Meta, Pods-Felder); Einträge suchen/lesen/anlegen/bearbeiten; veröffentlichen/zurückziehen/planen; duplizieren und Kopie gegen Original tauschen; Papierkorb; Revisionen |
| 3 | Menüs | anlegen, Einträge deklarativ setzen, Positionen zuweisen |
| 4 | Medien | suchen, ansehen (inkl. Vorschaubild), Upload per URL und per Datei, Download, Metadaten/Alt-Texte (auch Stapel), Löschen mit Verwendungsprüfung |
| 5 | Divi-Seiten | Seitenbaum, Modul-Katalog, einfügen/ändern/verschieben/löschen/duplizieren, seitenübergreifendes Ersetzen mit Dry-Run |
| 6 | Divi Theme Builder | Vorlagen auflisten/anlegen/zuweisen, Layouts bearbeiten, Dynamic Content (Pods-Felder, Meta, Post-Daten) |
| 7 | Divi Library / globale Module | speichern, einfügen (Kopie/global), zwischen Seiten kopieren, in globales Modul umwandeln, ähnliche Abschnitte finden |
| 8 | Divi-Designsystem | globale Farben, Variablen, Presets lesen und schreiben; Snapshots |
| 9 | Divi-Theme-Optionen | Schriften, Größen, Farben, Buttons, Layout-Maße, Logo, Divi-SEO – über Whitelist |
| 10 | WP-Einstellungen | Allgemein, Lesen (Startseite), Permalinks, Diskussion, Website-Icon – über Whitelist |
| 11 | Rückmeldung | einheitliches `ToolResult`; `page_render_summary` |
| 12 | Protokoll | Änderungsprotokoll mit Aufbewahrungsdauer, Cron-Bereinigung, Leeren |
| 13 | Anleitungen | Server-Instructions, Tool `lit/guide`, MCP-Prompts |
### 3.2 Kann
- Google Fonts herunterladen und über Divis Font-Upload selbst hosten.
- Pods-Strukturen (Pods, Felder) anlegen.
- Signierte, zeitlich begrenzte Vorschau-Links für Entwürfe ohne Login.
- Freitext-Hinweise pro Seite (z. B. Tonalität, Du/Sie) als Ergänzung der Server-Instructions.
### 3.3 Später
- SEO-Plugins (Yoast, Rank Math) als eigene Integrationen.
- „Fast gleich“-Erkennung von Abschnitten mit Detail-Unterschieden.
- Tickets und wiederkehrende Termine (The Events Calendar Pro).
- Umstieg auf MCP-Protokoll 2026-07-28 (sobald der Adapter es unterstützt, siehe §11).
### 3.4 Nicht im Umfang
- Benutzerverwaltung (Sicherheitsrisiko; ggf. später mit eigenem Konzept).
- Mehrsprachigkeit (WPML/Polylang).
- Divi Premade Layouts.
- Divi 4 / Shortcode-Inhalte (werden gelesen/erhalten, aber nicht bearbeitet).
## 4. Architektur
### 4.1 Bausteine auf der Kundenseite
```
claude.ai ──OAuth 2.1 (CIMD, PKCE)──▶ wp-media/mcp-oauth (Autorisierung, Token → WP-Benutzer)
│
▼
wordpress/mcp-adapter (MCP-Protokoll, Streamable HTTP)
│
▼
WP-LIT-MCP (Abilities, Server-Konfiguration, Admin)
```
- WP-LIT-MCP bindet `wp-media/mcp-oauth` und `wordpress/mcp-adapter` per Composer ein (Jetpack-Autoloader, `vendor/autoload_packages.php`). Ausgeliefert wird ein einzelnes Plugin.
- WP-LIT-MCP enthält **keine eigene OAuth-Implementierung**. Die Anbindungsschicht ist austauschbar; Rückfalloption ist `akirk/mcp-connect` (separates Plugin, nutzt allerdings das inzwischen veraltete Dynamic Client Registration).
- Versionen werden in `composer.lock` fixiert; jedes Update durchläuft den Konformitätstest (§11.4).
### 4.2 Anpassungen an wp-media/mcp-oauth
Die Bibliothek (v1.1.x) hat Lücken, die WP-LIT-MCP schließt:
| Lücke | Lösung |
|---|---|
| Kein Rollen-Gate (jeder eingeloggte Benutzer kann autorisieren) | Eigene Capability `lit_mcp_use`, vergeben an die in den Einstellungen freigegebenen Rollen. Gesetzt über die Adapter-Filter `mcp_adapter_{discover_abilities,get_ability_info,execute_ability}_capability` und zusätzlich in jeder Ability geprüft. |
| Erzeugtes Application Password gilt für alle REST-Routen (Issue #62) | Code-Analyse (v1.1.2): Das Klartext-Passwort wird verworfen, die Token-Prüfung läuft nur in der MCP-Route – vermutlich nicht ausnutzbar. Etappe 0 prüft das per Ende-zu-Ende-Test (Bearer-Token gegen `/wp/v2/users/me` muss 401 liefern). Nur falls der Test fehlschlägt: Filter auf `rest_authentication_errors`, der Bearer-Requests außerhalb der MCP-Route abweist. |
| Server fest mit drei Meta-Tools, keine direkten Tools | WP-LIT-MCP registriert den Server `mcp-oauth-server` auf `mcp_adapter_init` mit Priorität < 10 selbst, mit `OAuthHttpTransport` und eigener Tool-Liste. Folge bis upstream PR #63: harmloser „duplicate server“-Log-Eintrag. |
| Adapter auf `^0.5` gepinnt | Akzeptiert; Beobachtung der Upstream-Entwicklung. |
Ob dieser Aufbau stabil funktioniert, entscheidet Etappe 0 (§12).
### 4.3 Tool-Exposition (hybrid)
- **Direkte Tools (~20):** mit vollem Schema sofort sichtbar; nur direkte Tools können Bilder (`ImageContent`) zurückgeben. Standard-Set:
`lit/guide`, `lit/content-types`, `lit/content-query`, `lit/content-get`, `lit/content-create`, `lit/content-update`, `lit/post-publish`, `lit/media-search`, `lit/media-preview`, `lit/media-upload-from-url`, `lit/divi-outline`, `lit/divi-node-get`, `lit/divi-insert`, `lit/divi-update`, `lit/divi-move`, `lit/divi-delete`, `lit/divi-design-system`, `lit/divi-catalog`, `lit/library-insert`, `lit/page-render-summary`.
- **Stand nach Etappe 2 (Entscheidung D2, 2026-09-26):** 21 direkte Tools – das Standard-Set oben ohne `lit/library-insert` (kommt mit Etappe 3), dazu `lit/site-status` (leeres Eingabeschema, Anker der Verbindungstests) und `lit/media-download` (eingebettete Ressource, funktioniert nur direkt). `lit/divi-duplicate-node` und `lit/divi-replace-text` sind indirekt. Mit Etappe 3 sind es 22; das Budget wird dort erneut geprüft (Kosten entstehen vor allem durch große Eingabeschemas, nicht durch die Zahl). **Stand nach Etappe 3a:** 22 direkte Tools (dazu `lit/library-insert`); indirekt neu `lit/library-list`, `lit/library-save`, `lit/divi-node-copy`, `lit/divi-dynamic-sources`. Neue Tool-Gruppe „Divi-Bibliothek“ (`lit/library-*`); `lit/divi-node-copy` und `lit/divi-dynamic-sources` gehören zur Gruppe „Divi“.
- **Alle übrigen Abilities:** über die Meta-Tools des Adapters (`discover-abilities`, `get-ability-info`, `execute-ability`).
- In den Einstellungen änderbar, welche Abilities direkt, indirekt oder deaktiviert sind.
- Ability-Namen: `lit/<bereich>-<aktion>`. Der Adapter bildet daraus Tool-Namen (`lit-<bereich>-<aktion>`).
### 4.4 Plugin-Struktur
- Composer, PSR-4 (`LIT\MCP\` → `src/`), PHP ≥ 8.1, WordPress ≥ 6.9 (getestet mit 7.0).
- Kein Acorn (Größe, Konfliktrisiko auf fremden Seiten, kein Nutzen für ein Plugin ohne UI-Schwerpunkt).
```
wp-lit-mcp.php Plugin-Header, Bootstrap
src/
Core/
Plugin.php Start, Hooks, Abhängigkeitsprüfung
IntegrationRegistry.php sammelt verfügbare Integrationen
AbilityRegistrar.php registriert Abilities + Kategorien, Server-Konfiguration
Access.php Rollen-Gate, Capability lit_mcp_use, App-Password-Scope-Filter
ToolResult.php einheitliche Rückmeldung
ToolError.php verwertbare Fehler
ChangeLog/ Tabelle, Repository, Cron-Bereinigung
Guides/ Guide-Loader, Server-Instructions, Prompts
Admin/ Einstellungsseite, Protokoll-Ansicht (Settings API, WP_List_Table)
Integrations/
Integration.php Interface: isAvailable(), abilities(), prompts()
(Sonderfälle innerhalb einer Integration: abilities() lässt nicht nutzbare Tools einfach weg)
WordPress/ Content, Schema, Status/Kopie, Revisionen, Menüs, Einstellungen
Media/ Medien-Tools, Verwendungssuche
Divi/
BlockTree/ Parser, Serializer, Node, Pfad-Adressierung (reines PHP)
Schema/ Modul-Katalog, Attributpfad-Validierung
Compose/ Kurzform → Blöcke, Platzhalter-Auflösung
Pages/ Seiten-Tools, Speicher-Pipeline
Library/ Library, globale Module, find_similar
ThemeBuilder/ Vorlagen, Zuweisungen, Dynamic Content
Design/ Farben, Variablen, Presets, Theme-Optionen, Snapshots
Pods/ Feld-Adapter für die Schema-Erkennung, Schreiben via Pods-API
docs/guides/*.md Anleitungen für lit/guide
tests/Unit, tests/Integration, tests/Fixtures
```
### 4.5 Rechte (dreistufig)
1. **Verbinden:** mcp-oauth-Autorisierung (jeder eingeloggte Benutzer).
2. **Nutzen:** Capability `lit_mcp_use` (nur freigegebene Rollen) – geprüft über Adapter-Filter und in jeder Ability.
3. **Handeln:** konkrete Prüfung pro Ability, z. B. `edit_post` für die Ziel-ID, `publish_post` fürs Veröffentlichen, `upload_files`, `edit_theme_options` für Divi-Designsystem und Menüs, `manage_options` für WP-Einstellungen, `edit_others_posts` für Theme Builder (Divi-Anforderung).
## 5. Divi-Kern
### 5.1 Grundidee
Seiten, Theme-Builder-Layouts (`et_header_layout`, `et_body_layout`, `et_footer_layout`) und Library-Einträge (`et_pb_layout`) speichern Block-Markup in `post_content`. Alle Divi-Bearbeitungstools arbeiten auf jeder dieser Post-Arten über die `post_id`.
### 5.2 BlockTree
- Parst `post_content` in einen Baum (auf Basis von `parse_blocks`, mit Sonderbehandlung für `divi/global-layout` und `globalModule`, die `parse_blocks` laut Divi-Code nicht korrekt verarbeitet).
- Serialisiert den Baum zurück; unveränderte Knoten werden aus ihrem Original-Markup übernommen.
- **Invariante:** Parse → Serialize ohne Änderung ergibt byte-identischen Inhalt. Fremde/unbekannte Blöcke bleiben unberührt. Ausnahme beim Speichern (nicht im BlockTree): Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (`module.decoration.attributes`, `module.advanced.html` mit `htmlBefore`/`htmlAfter`/`elementType`) – Divi parst und serialisiert dann die ganze Seite neu, was nur Bytes ändert, die noch nicht in Divis `serialize_blocks()`-Form vorlagen; gemeldet als `CHANGED_ON_SAVE` (Ruling S7).
- Reines PHP ohne WordPress-Abhängigkeit (vollständig unit-testbar).
- Attributwerte mit `$variable(...)$` werden beim Serialisieren wie von Divi erwartet escaped (`"` → `"`).
- Umsetzung (D3): eigener Tokenizer mit dem Delimiter-Muster von `WP_Block_Parser` statt `parse_blocks()` – ohne WordPress testbar, jedes Byte zwischen den Delimitern bleibt erhalten. Knoten mit `globalModule`, `divi/global-layout` oder nicht dekodierbaren Attributen sind undurchsichtig: sie werden nicht betreten, nur als Ganzes verschoben, dupliziert oder gelöscht (bearbeitet werden globale Module seit Etappe 3a im Bibliothekseintrag, E2). Unbalanciertes Markup wird abgelehnt, nie repariert.
### 5.3 Adressierung und optimistische Sperre
- `lit/divi-outline` liefert den Baum mit Pfad-IDs (`s0`, `s0.r1`, `s0.r1.c0.m2`), Modultyp, Admin-Label, Textauszug und Preset-Namen.
- Jede Antwort enthält `tree_rev` = Hash des aktuellen `post_content` (Sperre der Divi-Werkzeuge) und `rev` = Sperre des ganzen Eintrags (`Rev::ofPost`, dieselbe wie bei `lit/content-get` und `lit/content-update`; Ruling P16).
- Jedes Divi-Schreib-Tool verlangt `tree_rev`. Weicht sie ab, wird abgelehnt (`rev_mismatch`), nichts geschrieben; Claude liest neu. Pfad-IDs gelten immer relativ zu einer `tree_rev`. Jede Schreib-Antwort liefert die neue `tree_rev` und die neuen Pfad-IDs der geänderten Knoten.
- Buchstaben der Pfad-IDs: `s` Abschnitt, `r` Zeile (auch `row-inner`), `c` Spalte (auch `column-inner`), `m` Modul (auch Kind-Module und Gruppen), `g` globales Modul bzw. globales Layout, `x` fremder Block (z. B. Block-Editor); gezählt wird je Buchstabe innerhalb des Elternknotens, `divi/placeholder` ist transparent.
### 5.4 Modulbeschreibung
**Kurzform** (kuratiertes Mapping für ~20 Module: Text, Heading, Button, Image, Blurb, CTA, Icon, Divider, Testimonial, Team Member, Accordion(+Item), Tabs(+Tab), Toggle, Gallery, Slider(+Slide), Contact Form(+Field), Map, Video, Number Counter, Social Media Follow(+Item), Icon List(+Item)):
```json
{ "type": "button", "text": "Termin buchen", "url": "/kontakt",
"preset": "CTA primär", "label": "Hero – Button" }
```
**Vollform** (alle Module, alle Einstellungen, Divis Attributpfade):
```json
{ "type": "heading",
"attrs": { "title.decoration.font.font": {
"desktop": { "color": "{{color:primary}}" },
"phone": { "size": "28px" } } } }
```
- Attributpfade werden gegen den Katalog (`get_all_core_modules_metadata`) validiert; unbekannte Pfade → `invalid_attr_path` mit Vorschlägen.
- Responsive-Werte über die Schlüssel `desktop` / `tablet` / `phone` (optional `hover`, `sticky`, wo das Modul es unterstützt).
- Kurz- und Vollform sind kombinierbar (Kurzform + zusätzliche `attrs`).
**Struktur:** Abschnitt → Zeilen (`"columns": "1_3,2_3"`) → Spalten → Module. Spezialabschnitte (fullwidth) über `"section_type"`.
**Platzhalter** (vom Plugin in Divis `$variable(...)$`-Syntax übersetzt):
| Platzhalter | Ergebnis |
|---|---|
| `{{color:primary}}`, `{{color:<Name>}}` | globale Farbe (`gcid-…`; Aliase primary/secondary/heading/body/link) |
| `{{var:<Name>}}` | globale Variable (`gvid-…`), Suche über Label |
| `{{font:heading}}`, `{{font:body}}` | Theme-Schriften (`--et_global_heading_font` / `--et_global_body_font`) |
| `{{post:title}}`, `{{post:excerpt}}`, `{{post:date}}`, … | Dynamic Content (Post-Daten) |
| `{{field:<meta_key>}}` | Dynamic Content `custom_meta_<meta_key>` (Pods/Meta) |
| `{{link:<ID>}}` | interner Link: Dynamic Content `post_link_url_<Typ>` mit `post_id` (nur Link-Felder; Ziel muss für den Benutzer lesbar sein, nicht im Papierkorb, kein automatischer Entwurf, kein Anhang; Entwürfe/private Ziele rendert Divi als `?page_id=`; Etappe 3a, E13a) |
| `"preset": "<Name>"` | `modulePreset: ["<id>"]` |
Unbekannte Namen → Fehler mit Liste der vorhandenen.
### 5.5 Qualitätsregeln und Warnungen
Jede Schreib-Rückmeldung enthält Warnungen zu:
- festen Farben/Schriften, wenn eine passende Variable existiert,
- Bildern ohne Alt-Text,
- leeren Modulen,
- Sprüngen in der Überschriftenhierarchie,
- Verwendung des Code-Moduls,
- ähnlichen, bereits existierenden Abschnitten (Hinweis auf `lit/library-find-similar`) – ab Etappe 3 zusammen mit `lit/library-find-similar` (D6).
Kurzform setzt Admin-Labels automatisch (`<Abschnitt> – <Modul>`), sofern nicht angegeben.
### 5.6 Speicher-Pipeline
1. Rechte prüfen (`edit_post`), `tree_rev` prüfen.
2. Änderung auf dem BlockTree ausführen, validieren (alles oder nichts).
3. Serialisieren, `wp_slash()`, `wp_update_post()` – Status unverändert, vorher Wiederherstellungspunkt (Revision). Für Benutzer ohne `unfiltered_html` wird kses für genau diesen Aufruf abgeschaltet: `filter_block_content()` würde sonst jeden unveränderten Block neu serialisieren (Byte-Identität, globale Module; in DDEV nachgewiesen). Das ist nur vertretbar, weil keine client-gelieferte Zeichenkette ungeprüft in `post_content` gelangt (D7): Klartext-Werte werden je Feldtyp bereinigt (Text: `sanitize_text_field`; Rich-Text: `wp_kses_post`, abhängig von `unfiltered_html`; URLs: `esc_url_raw`, geroutet über die Attribut-Metadaten des Katalogs, ersatzweise über den Schlüsselnamen). Werte mit `$variable(...)$` – ob aus Platzhaltern erzeugt oder unverändert aus `lit/divi-node-get` zurückgegeben – werden geparst, streng geprüft (zulässiger Typ, existierende gcid-/gvid-/Dynamic-Content-Quellennamen, whitelisted Settings-Schlüssel, String-Settings bereinigt) und vom Plugin neu serialisiert; alles andere wird mit `validation_failed` abgelehnt. Ob Divi das Dynamic-Content-`before`/`after` bei der Ausgabe selbst escaped, wird in Etappe 2b im Divi-Quellcode verifiziert; die Bereinigung gilt unabhängig davon (Verteidigung in der Tiefe, R2).
4. Post-Meta `_et_pb_use_divi_5=on`, `_et_pb_use_builder=on` setzen.
5. `ET_Core_PageResource::remove_static_resources($post_id, 'all', true)`; bei globalen Library-Elementen und Theme-Builder-Änderungen `('all', 'all', true)` sowie `et_theme_builder_clear_wp_cache('all')`.
6. Protokolleintrag, `ToolResult` zurückgeben.
### 5.7 Tools
| Ability | Art | Beschreibung |
|---|---|---|
| `lit/divi-outline` | lesen | Baum mit IDs, Labels, Textauszügen, `rev` und `tree_rev` |
| `lit/divi-node-get` | lesen | vollständige Attribute eines Knotens (Vollform) |
| `lit/divi-catalog` | lesen | Module, Kurzform-Felder, wichtige Attributpfade; Detail pro Modul |
| `lit/divi-design-system` | lesen | Farben, Variablen, Presets, Theme-Schriften/-Größen |
| `lit/divi-dynamic-sources` | lesen | verfügbare Dynamic-Content-Quellen für einen Post-Typ (über `DynamicContentOptions::get_options` mit einem Beispiel-Post des Typs) – Etappe 3a (E12, E14); nur textartige Felder (Antwort F4): Beziehungs-, Datei-, Tabellen- und unbekannte Felder stehen mit Grund unter `omitted`, Auswahlfelder liefern den gespeicherten Wert; Beispiel-Post nur einer, den der Benutzer bearbeiten darf |
| `lit/page-render-summary` | lesen | gerenderte Seite als strukturierter Auszug (Überschriften, Texte, Links, Bilder+Alt); Inhaltsbereich, im Prozess über `the_content` gerendert (ohne Header/Footer/Theme Builder); Teil der WordPress-Integration, funktioniert auch ohne Divi (D5) |
| `lit/divi-insert` | schreiben | Knoten (Kurz-/Vollform, auch ganze Abschnitte) an Position `parent`/`before`/`after` einfügen |
| `lit/divi-update` | schreiben | Werte setzen/entfernen (Kurz- oder Vollform) |
| `lit/divi-move` | schreiben | Knoten verschieben (auch zwischen Zeilen/Abschnitten) |
| `lit/divi-delete` | schreiben, destruktiv | Knoten entfernen |
| `lit/divi-duplicate-node` | schreiben | Knoten duplizieren |
| `lit/divi-replace-text` | schreiben | Text seitenübergreifend (je Inhaltstyp) ersetzen, `dry_run` (Standard `true`) liefert Treffer je Seite mit `tree_rev`; ausgeführt wird nur auf diesen `targets` mit `confirm: true`, jede Seite mit eigener Revision (D10) |
| `lit/library-list` | lesen | Einträge nach Typ (`layout_type`) und Scope, mit `cursor`; mit `id` Detail inkl. Knoten-Übersicht und betroffener Seiten (ersetzt `lit/library-get`, E9); nur Einträge mit `edit_post` |
| `lit/library-save` | schreiben | Knoten als Library-Eintrag speichern (normal oder global; global braucht Divis „Globale Bibliothekselemente bearbeiten“, P7); Knoten auf der Seite bleibt unverändert; StoredNodeGuard (E6) |
| `lit/library-insert` | schreiben | Library-Eintrag als Kopie oder globale Instanz im Format des Visual Builders (`localAttrs`-Schnappschuss, `globalParent`) einfügen; direkt |
| `lit/divi-node-copy` | schreiben | Knoten von Eintrag A nach Eintrag B kopieren (Seiten und Bibliothekseinträge; byte-gleich, `source_tree_rev` Pflicht, S7/S26-Prüfung E6) |
| `lit/library-make-global` | schreiben | Knoten in globales Modul umwandeln; optional gleichartige Vorkommen ersetzen |
| `lit/library-find-similar` | lesen | ähnliche Abschnitte seitenübergreifend (Struktur-Fingerabdruck: Modultypen, Aufbau, Presets) |
| `lit/tb-list` | lesen | Vorlagen, Bedingungen, Layouts |
| `lit/tb-create-template` | schreiben | Vorlage mit Header/Body/Footer-Layouts anlegen (`et_theme_builder_insert_layout`, `et_theme_builder_store_template`, Meta `_et_template`) |
| `lit/tb-assign` | schreiben | Bedingungen (`_et_use_on`/`_et_exclude_from`, z. B. `singular:post_type:person:all`) setzen |
## 6. WordPress-Basis
### 6.1 Schema-Erkennung (`lit/content-types`)
- Post-Typen mit `show_ui`: Name, Labels, `supports`, hierarchisch, Divi-fähig, Anzahl.
- Taxonomien pro Typ.
- Felder pro Typ, zusammengeführt aus:
- `get_registered_meta_keys()` (Kern),
- **Pods-Adapter:** Feldtyp, Pflicht, Auswahlwerte, Beziehungsziel, Mehrfachwert (über Pods-API),
- gefundenen Meta-Schlüsseln (Stichprobe; `_`-Schlüssel ausgeblendet, sofern kein Adapter sie kennt).
- Status pro Feld: `writable`, `readonly`, `unknown` (Schreiben erlaubt, mit Warnung).
### 6.2 Content-Tools (jeder Post-Typ)
| Ability | Beschreibung |
|---|---|
| `lit/content-query` | Filter: Typ, Status, Suche, Taxonomie, Feldwert, Datum; Cursor-Paginierung |
| `lit/content-get` | Kerndaten, Terms, Felder, Beitragsbild; bei Divi-Inhalt Verweis auf `lit/divi-outline` |
| `lit/content-create` | neuer Eintrag, **Standard: Entwurf**; Felder gegen Schema validiert; optional Divi-Inhalt in Kurzform |
| `lit/content-update` | Kerndaten, Terms, Felder ändern (Status unverändert) |
| `lit/post-publish`, `lit/post-unpublish`, `lit/post-schedule` | Statuswechsel, explizit |
| `lit/post-duplicate` | Entwurfskopie inkl. Meta, Terms, Divi-Flags |
| `lit/post-replace-with-copy` | Inhalt/Meta/Terms der Kopie ins Original übernehmen (ID, URL, Menüeinträge bleiben); Original erhält vorher Revision; Kopie → Papierkorb |
| `lit/post-trash` | nur Papierkorb, nie endgültig löschen |
| `lit/revisions-list`, `lit/revision-restore` | Revisionen |
- Pods-Felder werden über die Pods-API geschrieben (Beziehungen, Caches), sonstige Felder über die Meta-API.
- Erweiterungspunkt `lit_mcp_after_save` (Action): Integrationen können abgeleitete Felder nachberechnen (z. B. falls The Events Calendar dies erfordert – Prüfung in Etappe 0).
### 6.3 Medien
| Ability | Beschreibung |
|---|---|
| `lit/media-search` | Text, MIME-Typ, Datum, `missing_alt`, `unused` |
| `lit/media-get` | Metadaten, Größen, URLs, Verwendungsorte |
| `lit/media-preview` | verkleinertes Bild als `ImageContent` (direktes Tool, kein `outputSchema`) |
| `lit/media-upload-from-url` | Import via `wp_safe_remote_get` (SSRF-Schutz), MIME-Whitelist, Größenlimit |
| `lit/media-upload` | Base64-Upload, Limit einstellbar (Standard 5 MB) |
| `lit/media-download` | Original oder Größe als eingebettete Ressource (Blob) |
| `lit/media-update` | Alt, Titel, Bildunterschrift, Beschreibung – auch Stapel |
| `lit/media-delete` | nur mit `confirm: true`; bei Verwendung abgelehnt außer `force: true` |
Verwendungssuche: Anhang-ID in Divi-Attributen, Beitragsbildern, Galerie-Feldern und Meta.
Divi-Bildmodule, Hintergründe und Galerien referenzieren Bilder per Anhang-ID (plus URL, wie Divi es speichert).
### 6.4 Menüs
`lit/menu-list`, `lit/menu-get` (Baum), `lit/menu-create`, `lit/menu-set-items` (deklarativer Gesamtbaum oder Einzeloperationen hinzufügen/verschieben/entfernen), `lit/menu-assign-location`.
### 6.5 WordPress-Einstellungen
`lit/wp-settings-get`, `lit/wp-settings-update` über Whitelist:
- Allgemein: `blogname`, `blogdescription`, `WPLANG`, `timezone_string`, `date_format`, `time_format`
- Lesen: `show_on_front`, `page_on_front`, `page_for_posts`, `posts_per_page`
- Diskussion: `default_comment_status`, `default_ping_status`
- Permalinks: `permalink_structure` (danach `flush_rewrite_rules()`)
- Website-Icon: `site_icon`
Gesperrt: `admin_email`, `users_can_register`, `default_role`, `siteurl`, `home`.
## 7. Divi-Designsystem und Theme-Optionen
| Ability | Beschreibung |
|---|---|
| `lit/divi-colors-set` | globale Farben anlegen/ändern inkl. der fünf Hauptfarben (über `GlobalData::set_global_colors`) |
| `lit/divi-variables-set` | Variablen (numbers, strings, images, links, fonts) über `GlobalData::set_global_variables` |
| `lit/divi-presets-set` | Modul- und Gruppen-Presets anlegen/ändern, per Name (über `GlobalPreset::save_data`) |
| `lit/divi-theme-options-get`, `lit/divi-theme-options-update` | Whitelist mit Typ und Beschreibung pro Feld (s. u.) |
| `lit/divi-design-snapshots`, `lit/divi-design-restore` | Snapshots auflisten und wiederherstellen |
| `lit/divi-font-upload-google` (Kann) | Google-Schrift (TTF) laden, über `FontsUtility::font_add` ablegen |
Theme-Options-Whitelist (Auszug, vollständige Liste in der Implementierung mit Typen):
- Schriften: `heading_font`, `body_font`, `heading_font_weight`, `body_font_weight`, `primary_nav_font`, `secondary_nav_font`, `all_buttons_font`
- Größen: `body_font_size`, `tablet_body_font_size`, `phone_body_font_size`, `body_header_size`, `tablet_header_font_size`, `phone_header_font_size`, `body_font_height`, `body_header_height`, `body_header_spacing`, `body_header_style`
- Layout: `content_width`, `gutter_width`, `section_padding`, `row_padding`
- Farben: `accent_color`, `secondary_accent_color`, `header_color`, `font_color`, `link_color`
- Buttons: `all_buttons_*` inkl. `*_hover`
- Logo: `divi_logo`
- SEO: `divi_seo_home_*`, `divi_seo_single_*`, `divi_seo_index_*`
Regeln:
- Schreiben ausschließlich über Divis eigene Funktionen; danach `ET_Core_PageResource::remove_static_resources('all', 'all', true)`.
- Vor jeder Änderung wird der betroffene Bereich (Farben, Variablen, Presets oder Theme-Optionen) als **Snapshot** gesichert (eigene Option, begrenzte Anzahl, Standard 20). Snapshot-ID im `ToolResult` und im Protokoll.
- Voraussetzung: `edit_theme_options`; für Variablen zusätzlich Divis Rollen-Recht `variables_manager`.
Ablauf „Corporate Design übernehmen“ (Prompt, §9.3): Claude liest das CD im Chat → `lit/divi-design-system` → schlägt Zuordnung vor → nach Bestätigung Schreiben über die Tools oben.
## 8. Rückmeldung, Fehler, Protokoll, Einstellungen
### 8.1 ToolResult
```json
{ "ok": true,
"summary": "Button in 'Hero' aktualisiert",
"changed": [{ "id": "s0.r0.c0.m2", "type": "divi/button", "action": "updated", "label": "Hero – Button" }],
"outline": "…betroffener Abschnitt…",
"rev": "a1b2c3", "tree_rev": "d4e5f6",
"warnings": ["Feste Farbe #ff0000 – Variable 'Primär' ist ähnlich"],
"revision_id": 812, "snapshot_id": null, "log_id": 4711,
"preview_url": "…", "edit_url": "…&et_fb=1" }
```
Rückgabe als `structuredContent` plus kurze Text-Zusammenfassung. Listen immer mit `cursor`/`limit`.
### 8.2 Fehler
- Fachliche Fehler → Tool-Ergebnis mit `isError: true` und `{ code, message, hint, fields? }`. Codes: `rev_mismatch`, `invalid_attr_path`, `validation_failed`, `forbidden`, `not_found`, `in_use`, `confirm_required`, `unsupported`.
- Alles oder nichts pro Aufruf; Stapel-Tools melden Ergebnis pro Eintrag.
- Ausnahmen aus WordPress/Divi werden abgefangen, protokolliert (PHP-Error-Log) und neutral gemeldet (kein Stacktrace an den Client).
### 8.3 Änderungsprotokoll
- Tabelle `{prefix}lit_mcp_log`: `id`, `created_at`, `user_id`, `client` (Name aus OAuth), `ability`, `object_type`, `object_id`, `summary`, `revision_id`, `snapshot_id`, `result`.
- Keine Inhalte, nur Verweise.
- Aufbewahrung einstellbar (Standard 90 Tage), täglicher WP-Cron-Job, Button „Protokoll leeren“.
- Admin-Ansicht (`WP_List_Table`), filterbar, Links zu Revision/Wiederherstellen.
### 8.4 Einstellungsseite (Einstellungen → LIT MCP)
- **Verbindung:** Status Adapter/OAuth, MCP-Server-URL zum Kopieren, Site-Health-Test (Discovery-Dokumente erreichbar, 401 mit `WWW-Authenticate`).
- **Zugriff:** erlaubte Rollen (Standard: Administrator); aktive Verbindungen je Benutzer mit Widerruf (über die von mcp-oauth erzeugten Application Passwords).
- **Tools:** direkt / indirekt / deaktiviert je Ability. (Das Divi-Code-Modul ist immer erlaubt, nur nachrangig – keine Einstellung.)
- **Limits:** Upload-Größe, Protokoll-Aufbewahrung, Anzahl Snapshots.
- Umsetzung mit Settings API, ohne JS-Framework.
## 9. Anleitungen für Claude
### 9.1 Server-Instructions
Dynamisch pro Seite erzeugt (Server-Beschreibung → `instructions`), ca. 15 Zeilen:
- aktive Integrationen und Post-Typen,
- Kernregeln: vor dem Schreiben `lit/divi-outline` und `lit/divi-design-system` lesen; Variablen/Presets vor festen Werten; native Module vor Code-Modul; Status nie ändern ohne ausdrücklichen Wunsch; immer `rev` (bei Divi-Änderungen `tree_rev`) mitgeben,
- Verweis auf `lit/guide`.
- Etappe 3a: Zeile „Divi-Bibliothek: …“ (globale Module im Bibliothekseintrag ändern, Probelauf und `confirm: true`, Guide `divi-library`), nur wenn die Gruppe „Divi-Bibliothek“ aktiv und beschreibbar ist.
### 9.2 `lit/guide(topic)`
Direktes Tool; Anleitungen als Markdown in `docs/guides/`, mit dem Plugin versioniert, per Filter `lit_mcp_guides` erweiterbar.
Themen: `overview`, `divi-structure`, `divi-shortform`, `divi-attrs`, `divi-placeholders`, `divi-design-system`, `divi-library`, `theme-builder`, `content-types`, `media`, `workflows`. Thema `all` liefert alle Themen in einem Aufruf (Etappe 3a).
### 9.3 MCP-Prompts
`corporate-design-uebernehmen`, `neue-website-aufsetzen`, `seite-pruefen`, `globale-module-vorschlagen`, `custom-post-type-vorlage`.
(Stand Etappe 3a: noch keine MCP-Prompts im Plugin; `globale-module-vorschlagen` steht als Ablauf im Guide `divi-library`.)
## 10. Sicherheit
- OAuth 2.1 mit PKCE über mcp-oauth; Tokens an den MCP-Server gebunden; kein Token-Passthrough an Dritte.
- Rollen-Gate + Rechteprüfung pro Ability (§4.5).
- App-Password-Scope auf die MCP-Route beschränkt (§4.2).
- Whitelists für WP-Einstellungen und Divi-Theme-Optionen.
- Uploads: `wp_safe_remote_get`, MIME-Whitelist, Größenlimits.
- Destruktive Aktionen: `confirm: true` erforderlich; kein endgültiges Löschen von Posts.
- Eingaben werden gegen JSON Schema und fachlich validiert; Bereinigung von Divi-Schreibwerten siehe §5.6/D7 (eine Quelle der Wahrheit): je Feldtyp (`sanitize_text_field`/`wp_kses_post`/`esc_url_raw`, URLs geroutet über Katalog-Metadaten bzw. Schlüsselname), `$variable(...)$`-Werte geparst/geprüft/neu serialisiert, sonst `validation_failed`.
- HTTPS erforderlich (Hinweis im Admin, wenn nicht aktiv).
## 11. MCP-Konformität
Referenz: https://modelcontextprotocol.io/specification
### 11.1 Zielversion
- **Sofort:** Protokollversion **2025-11-25** (vom Adapter unterstützt).
- **Später:** **2026-07-28** (zustandslos, `server/discover`, CIMD bevorzugt), sobald der Adapter sie unterstützt. Der Umstieg ist ein eigenes Arbeitspaket.
### 11.2 Verantwortung
| Bereich | Zuständig | Sicherung |
|---|---|---|
| JSON-RPC, Transport (Streamable HTTP), Versionsverhandlung, Header, Origin-Prüfung | MCP Adapter | Versionen fixiert, Konformitätstest pro Update |
| Autorisierung: OAuth 2.1, PKCE, RFC 9728/8414-Discovery, CIMD, Audience-Bindung | mcp-oauth | Etappe 0 prüft u. a. `WWW-Authenticate` bei 401, `resource`-Parameter (RFC 8707), `iss` (RFC 9207); Lücken upstream melden oder per Filter ergänzen |
| Tools, Prompts, Ergebnisse | WP-LIT-MCP | §11.3 |
### 11.3 Anforderungen an WP-LIT-MCP
- `inputSchema` für jedes Tool, `outputSchema` wo strukturiert (JSON Schema 2020-12); Ergebnisse als `structuredContent` + Text.
- Fachliche Fehler als `isError: true`, Protokollfehler als JSON-RPC-Fehler.
- Annotations korrekt: `readOnlyHint` (Lese-Tools), `destructiveHint` (Löschen, Ersetzen, Austausch), `idempotentHint`.
- Zustandslos: kein serverseitiger Sitzungszustand; `rev`/`tree_rev` als explizite, servererzeugte Handles in Tool-Argumenten.
- Deterministische Reihenfolge der Tool-Liste.
- Cursor-Paginierung für Listen.
- Tool-Namen spezifikationskonform und eindeutig (`lit-…`).
- Bestätigungen vorerst über `confirm: true`; Multi-Round-Trip-Elicitation erst, wenn Adapter und claude.ai sie unterstützen.
### 11.4 Konformitätstests
- MCP Inspector gegen den Server (Etappe 0 und bei jedem Update von Adapter/mcp-oauth).
- Automatischer Test je Ability: Ergebnis gegen `outputSchema` validiert.
- Ende-zu-Ende-Test mit echtem claude.ai-Connector.
## 12. Etappen
| Etappe | Inhalt | Ergebnis |
|---|---|---|
| **0 – Anbindung testen** | Plugin-Gerüst, Composer, mcp-oauth + Adapter, Rollen-Gate, App-Password-Scope-Filter, eigener Server mit direkten Tools, `lit/guide` (overview), ein Test-Tool; claude.ai-Connector über `ddev share`/Staging; MCP Inspector; Test: The Events Calendar-Eintrag generisch anlegen | Entscheidung: mcp-oauth bleibt oder Wechsel zu mcp-connect; Events-Erweiterungspunkt nötig ja/nein |
| **1 – WordPress-Basis** | Schema-Erkennung inkl. Pods, Content-Tools, Status/Kopie/Austausch, Revisionen, Medien, Menüs, WP-Einstellungen, `ToolResult`, Protokoll, Einstellungsseite | Seiten/Beiträge/Pods-Einträge pflegen, Medien verwalten, Websites mit Struktur und Menü aufsetzen |
| **2 – Divi-Seiten** | BlockTree, Katalog, Kurz-/Vollform, Platzhalter, Outline/Insert/Update/Move/Delete/Duplicate/Replace, Warnungen, `page-render-summary`, Divi-Guides (umgesetzt in 2a: lesen, 2b: schreiben) | Divi-Seiten mit Claude bauen und pflegen |
| **3 – Library & Theme Builder** | Library-Tools, Node-Copy, Make-Global, Find-Similar, Theme-Builder-Tools, Dynamic Content | CPT-Vorlagen bauen, globale Module einführen |
| **4 – Designsystem** | Farben, Variablen, Presets, Theme-Optionen inkl. SEO, Snapshots, Prompts | Corporate Design in einem Rutsch übernehmen |
| **U – Auslieferung & Updates** (nach Etappe 1) | **OFFEN – bewusst noch nicht spezifiziert.** Bekannt: Build mit `composer install --no-dev` (~3,3 MB vendor), Updates nur anzeigen (manueller Klick), `plugin-update-checker`; Hosting (Gitea / GitHub / eigene Subdomain mit `update.json`) und Signaturprüfung werden später entschieden | – |
Kann-Kriterien nach Etappe 4 oder bei freier Kapazität.
## 13. Tests
- **Unit (PHPUnit, Brain Monkey, Mockery):** BlockTree (Roundtrip-Invariante), Kurzform → Markup, Attributpfad-Validierung, Platzhalter, Schema-Merge, Whitelists, ToolResult.
- **Fixtures:** echte, im Divi Builder gebaute Seiten (inkl. globaler Module, Presets, Variablen, Dynamic Content) als Roundtrip- und Änderungs-Testdaten.
- **Integration (DDEV, WP-CLI/REST):** jede Ability gegen die lokale Installation; gerenderte Ausgabe enthält `et_pb_*`-Klassen.
- **Konformität:** §11.4.
- **Manuelle Abnahme pro Etappe:** Bearbeitbarkeit im Visual Builder, claude.ai-Ablauf.
- Qualitätswerkzeuge: phpcs (WPCS), PHPCompatibilityWP.
## 14. Risiken
| Risiko | Gegenmaßnahme |
|---|---|
| mcp-oauth jung, Lücken (Rollen, Scope, direkte Tools) | eigene Absicherungen (§4.2), Etappe 0 als Entscheidungspunkt, Rückfall mcp-connect |
| Adapter 0.7 bricht API, rät vom Bundling ab | Versionen fixieren, Konformitätstest, Umstieg geplant |
| Divi ändert Speicherformat | Divi-eigene Metadaten/Funktionen nutzen, Fixtures, Tests je Divi-Update, `builderVersion` setzen |
| `.well-known`/POST auf `/wp-json/` durch Hoster/WAF blockiert | Site-Health-Test, Fehlersuche-Doku |
| Design-Qualität neuer Abschnitte ohne Vorlagen | Library-first-Arbeitsweise, Designsystem-Platzhalter, Guides, Warnungen |
| Parallele Bearbeitung im Builder | `tree_rev`-Sperre |
| Datenformate Designsystem nur aus Code abgeleitet (lokal keine Daten) | vor Etappe 4 echte Daten im Builder anlegen und als Fixtures sichern |
## 15. Offene Punkte (in Etappen zu klären)
- Etappe 0: Funktioniert der Workaround für direkte Tools mit mcp-oauth stabil? Erfüllt mcp-oauth `iss`/`resource`-Anforderungen?
- Etappe 0: Erscheinen generisch angelegte The Events Calendar-Einträge korrekt?
- Etappe 2: erledigt – Kurzform-Felder aus Divis Conversion-Outlines abgeleitet (`ShortformMap`), gegen den Live-Katalog geprüft (`tests/Integration/divi-shortform-check.php`), dokumentiert im Guide `divi-shortform`.
- Etappe 4: Format der Presets/Variablen an echten Daten verifizieren.