Stand: 2026-08-20. Diese Seite ist die Quelle, nicht eine Kopie: Sie liegt als Artikel im CMS und wird dort gepflegt. Am ehesten altert die DNS-Notiz unter Stolperfallen. Der Werkzeugumfang altert hier nicht — maßgeblich ist
list_tools, das die Liste aus der laufenden Registry erzeugt (Stand dieser Fassung: 38 Werkzeuge).
Zweck: Dieses Blatt brauchst Du, wenn Du über das generic-cms-MCP Artikel anlegst, bearbeitest oder veröffentlichst, Bilder hochlädst oder Diagramme einbettest. Es dokumentiert Besonderheiten und Stolperfallen — die vollständige, immer aktuelle Werkzeugliste lieferst Du Dir selbst per list_tools oder über die Schemas der mcp__*-Werkzeuge.
Registrierung
claude mcp add --transport http generic-cms https://<cms-host>/mcp \
--header "Authorization: Bearer gcms_<dein-token>"
<cms-host>ist der Hostname Deiner CMS-Instanz — nicht der persönliche Login-Host.- Lokaler Scope (nur das aktuelle Projektverzeichnis). Für global:
--scope user. - Werkzeuge erscheinen erst nach einem Neustart der Sitzung. Ein Connector in claude.ai holt seine Werkzeugliste beim Verbindungsaufbau — nach einer Server-Änderung hilft nur ein Reconnect, kein Warten.
claude mcp listmuss ✓ Connected zeigen.
Unverhandelbar: Der Token wird nie ins Repo committet, nie in Memory oder CLAUDE.md abgelegt und nie in Artikeln zitiert. Er existiert ausschließlich im Header der Registrierung.
Warum so viele Werkzeuge?
38 sind mehr, als man einem Modell üblicherweise vorsetzt — die gängige Empfehlung lautet „wenige, mächtige Werkzeuge". Hier ist es bewusst andersherum: 37 der 38 Werkzeuge rufen genau eine REST-Route, keines ruft zwei, und keine Route wird von zwei Werkzeugen geteilt (list_tools ist das einzige rein lokale). Was das für Dich heißt:
- Ein Fehlschlag ist eindeutig. Der Status kommt von genau einer Route — kein Sammelwerkzeug, das Teilfehler für Dich deutet.
- Rechte sind sichtbar. Jedes Werkzeug trägt den Scope seiner Route. Fehlt Dir ein Recht, scheitert genau der eine Aufruf.
- Der Shim erfindet nichts. Er reicht das CMS durch; Antworten sind roh, dafür verlässlich.
- Suche die Werkzeuge, statt sie zu erraten. Der Preis dieser Bauweise ist ein langer Katalog.
list_toolsliefert ihn gruppiert mit Kurzbeschreibung — einmal am Sitzungsbeginn aufrufen ist billiger als jeder Rateversuch.
Werkzeugumfang
Quelle der Wahrheit ist list_tools. Die Gruppierung hier dient nur der ersten Orientierung.
| Bereich | Werkzeuge |
|---|---|
| Struktur & Discovery | list_tools, list_sections, create_section, update_section, list_categories, describe_frontmatter, list_article_classes, recommend_renderer |
| Artikel-Lifecycle | list_articles, create_article, read_article, update_article, preview_article, validate_article, lint_site, move_article, set_draft, delete_article, reindex_article |
| Papierkorb | list_trash, restore_article, purge_article |
| Medien | upload_image, upload_file, list_images, prune_images, mermaid_titelbild, uml_titelbild, dot_titelbild, d2_titelbild, vegalite_titelbild |
| Suche | search_articles (Meilisearch-Volltext; indexiert ist nur der Stand des letzten release_site) |
| Build & Betrieb | release_site, build_status, build_log, site_status, get_stats, audit_log |
Lesbare statt roher Antworten: list_trash, list_images, search_articles, audit_log, list_categories und list_tools nehmen format="text" und liefern dieselben Daten als gesetzten Text statt als JSON — rund ein Drittel der Zeichen. Zum Weiterverarbeiten der Felder bleibt der Vorgabewert "json" richtig. Die Werkzeuge mit outputSchema (u. a. list_articles, site_status, lint_site) haben den Parameter bewusst nicht — sie liefern stattdessen strukturierte Inhalte.
Diagramme — fünf Sprachen, eine Empfehlung
Mermaid ist der Standard und der sichere Rückfall, aber nicht die einzige Sprache. Vier weitere werden am Build zu SVG gerendert und automatisch eingebettet — kein Extra-Werkzeugaufruf nötig, es genügt der richtige Fence:
| Fence | Sprache | Wofür |
|---|---|---|
```mermaid | Mermaid | Standard: Abläufe, Zeitachsen, Mindmaps. Clientseitig gerendert. |
```uml / ```plantuml | PlantUML | UML im engeren Sinn: Sequenz, Klasse, Zustand. |
```dot / ```graphviz | Graphviz | Dichte Graphen, viele Knoten, hierarchische Layouts. |
```d2 | D2 | Node-Link mit Anspruch an Optik und Layout. |
```vegalite / ```vega-lite | Vega-Lite | Echte Datendiagramme aus gemessenen Zahlen (JSON-Spec). |
Welche passt? Frag recommend_renderer(task="…"), statt zu raten. Das Werkzeug rendert und schreibt nichts: Es liest die Absicht aus dem Prosa-Auftrag, schätzt die Datenform (nodes, edges, data_points — explizit übergeben oder aus dem Text gelesen) und liefert eine begründete Rangliste. Empfohlen wird nur, was in dieser Installation auch rendert; fällt ein Sidecar aus, verschwinden genau dessen Sprachen aus der Empfehlung und eine Notiz sagt, was fehlt. Das Feld fence ist die Angabe, die Du dann tatsächlich schreibst.
Vega-Lite: Daten müssen inline stehen. Eine Spec, die auf eine entfernte URL zeigt (
data: {url: …}), wird mit 422 abgewiesen — der Build holt beim Rendern nichts aus dem Netz, und ein Diagramm, dessen Zahlen woanders liegen, ist nicht reproduzierbar. Alsodata: {values: [ … ]}. Zweiter Stolperstein: Vega-Lite stellt die Beschriftungen einer diskreten X-Achse standardmäßig hochkant —"axis": {"labelAngle": 0}auf dem Kanal stellt sie waagerecht.
Jede der fünf Sprachen kann ihr Diagramm auch zum Titelbild machen: mermaid_titelbild, uml_titelbild, dot_titelbild, d2_titelbild, vegalite_titelbild. Alle rahmen in dieselbe 1200×630-Karte mit Artikeltitel und Marken-Fuß, sodass Hero-Bilder gleich aussehen, egal welcher Renderer gezeichnet hat. block_index="auto" wählt den Block, der die Karte am besten füllt; ein unveränderter Lauf antwortet mit skipped.
Artikelklassen
create_article akzeptiert optional article_class — eine Inhalts-Gattung mit eigenem Layout, Zusatzfeldern und Body-Gerüst (leerer body → Gerüst wird eingesetzt). Quelle der Wahrheit ist list_article_classes. Ohne Klasse entsteht ein normaler Artikel.
| Klasse | Wofür | Zusatzfelder (via extra_frontmatter) |
|---|---|---|
analyse | Bewertung mit Haltung: These, Befunde, Fazit-Box oben, Quellen-Fuß | fazit, quellen |
tutorial | Nummerierte Schritte zum Ergebnis; erzeugt HowTo-JSON-LD | schwierigkeit, dauer, voraussetzungen |
notiz | Kurztipp ohne Ballast — kein Hero, kein Inhaltsverzeichnis | — |
referenz | Versioniertes Nachschlagewerk mit Gültigkeitsstand — auch für „Don’t"-Wissen | version, stand, geltungsbereich, quellen |
projektlog | Chronologisches Projekt-Journal mit datierten Einträgen | projekt, status, zeitraum |
Frontmatter bei create_article
| Feld | Typ | Hinweis |
|---|---|---|
section | str (Pflicht) | Pfad aus list_sections, z. B. blog |
title | str (Pflicht) | Reader-sichtbar |
body | str (Pflicht) | Hugo-Markdown |
slug | str? | Optional — sonst aus dem Titel abgeleitet (Umlaut-Transliteration, Kleinschreibung, Bindestrich) |
article_class | str? | Inhalts-Gattung, siehe oben; setzt den Hugo-type |
kategorien | list[str]? | Tags/Kategorien |
lesezeit | int? | Minuten — weggelassen schätzt das Theme selbst |
titelbild | str? | Hero-Bild: exakt die url aus der upload_image-Antwort einsetzen — den Pfad nie selbst konstruieren |
extra_frontmatter | dict? | Beliebige zusätzliche YAML-Felder (draft, aliases, weight, Klassen-Zusatzfelder) |
Empfohlener Ablauf
validate_article(body=…, title=…)— Lint vor dem Anlegen; fängt kaputte Diagramme, H1 im Body und Frontmatter-Fehler, bevor Leser sie sehen.create_article(…)— für Änderungenupdate_article(ändert nur die übergebenen Felder). Nie delete+create für ein Edit. Der Body muss nicht komplett neu gesendet werden:find+replaceersetzt genau eine eindeutige Fundstelle. 0 Treffer → 400, >1 Treffer → 400, außer mitreplace_all=true. Deckt auch das Einfügen an einem Anker ab: Anker finden, durch Anker + neuen Text ersetzen.bodymitmode=append/prepend(Vorgabereplace= Ganz-Body-Ersatz).normalize=trueräumt einen Paste auf: mehrfache Leerzeilen → eine, Leerzeichen am Zeilenende weg.release=truestößt nach erfolgreichem Speichern direkt den Voll-Build an.expected_updatedschützt davor, die Änderung eines anderen zu überschreiben: denupdated-Wert aus demread_articlemitgeben, auf dem die Bearbeitung beruht.
release_site()→build_status(build_id=…, wait_seconds=120). Gestartet ist nicht fertig — nurstatus == "success"heißt, dass die Änderung auf der Website steht.- Verifikation per
read_article, nicht über die öffentliche URL (viele Instanzen stehen hinter einem Zugangsschutz).
Jede Antwort trägt ein site-Feld mit dem Hostnamen der antwortenden Instanz — prüfe vor Schreiboperationen, dass Du mit der richtigen sprichst. Das ist kein Schmuck: Wer mehrere Instanzen registriert hat, schreibt sonst irgendwann in die falsche.
Vollständiges Beispiel
// 1. Hero-Bild hochladen — die Antwort liefert die einzusetzende URL:
// {"uploaded": [{"url": "/images/mcp-setup-claude-code.jpg"}], "errors": []}
// 2. Artikel anlegen:
create_article(
section = "tipps",
title = "MCP-Setup für externe Claude-Sitzungen",
slug = "mcp-setup-claude-code",
kategorien = ["mcp", "claude-code", "hugo"],
lesezeit = 4,
titelbild = "/images/mcp-setup-claude-code.jpg",
extra_frontmatter = { "draft": false, "weight": 10 },
body = """
Kurzfassung: Bearer-Token registrieren, einmal die Sitzung neu starten, fertig.
```mermaid
flowchart LR
A[claude mcp add] --> B[Sitzung neu starten]
B --> C[Werkzeuge sichtbar]
C --> D[create_article]
D --> E[release_site]
E --> F[build_status abfragen]
```
## Schritte
1. Befehl ausführen.
2. Sitzung neu starten.
3. Werkzeuge über die Schemas erkunden.
"""
)
Zur Verschachtelung: Der innere ```mermaid-Fence steht bewusst innerhalb des dreifach gequoteten body-Strings. Beim Nachbauen die Fence-Ebenen nicht verwechseln.
Mermaid — Stolperfallen
Das Theme lädt mermaid@11 vom CDN — also die jeweils neueste 11.x, nicht eine feste Fassung. Der Renderer, der die Titelbilder zeichnet, pinnt dagegen eine konkrete Version. Beides ist Absicht, aber es heißt: ein Diagramm kann im Browser minimal anders aussehen als in seiner Hero-Karte. Der Parser von Mermaid 10+ ist deutlich strikter als 9.x; die folgenden Punkte sind die Quelle praktisch aller „Syntax error in text"-Fehler:
- Zeilenumbrüche in Node-Labels:
<br/>verwenden, nicht\n. - Spitze Klammern in Labels als
</>escapen. Einzige unescaped erlaubte Tags sind<br/>und<br>. - Datenbank-/Zylinderform:
DB[("Text")]— eckige plus runde Klammern um ein gequotetes Label. - Subgraph-Titel mit Leerzeichen quoten:
subgraph "Mein Titel". - Pipe
|oder Quote"im Label erzwingen Quoting des ganzen Labels:A["|d| sm"]. - Gantt: Tags gehören VOR die ID, nie dahinter. Also
Gate :milestone, g1, 2028-06, 0d— nichtGate :g1, milestone, 2028-06, 0d. Mermaid streift die Tags (milestone,crit,active,done,vert) nur am Anfang der Feldliste ab; steht die ID davor, bleiben vier Felder stehen, und Mermaid kennt nur ein bis drei. Der Haken: Gantt-Tasks werden erst im Renderer übersetzt, nicht im Parser — Validierung und Build bleiben grün, und der Leser bekommt das Bomben-Icon. Deshalb prüft der Lint das textuell (GANTT_TAG_ORDER,GANTT_TASK_FIELDS). - Der Code-Fence muss exakt
```mermaidheißen (kleingeschrieben, kein Leerzeichen dahinter).
Die häufigsten Modellfehler (Pipe-Labels, Subgraphs mit Leerzeichen, \n-Umbrüche, ungeschützte spitze Klammern) normalisiert das CMS beim Speichern automatisch. Semantische Fehlentscheidungen — Zylinder gegen Rechteck — erkennt der Sanitizer bewusst nicht.
Fehler-Schnellreferenz
| Symptom | Wahrscheinliche Ursache | Erste Reaktion |
|---|---|---|
409 bei release_site | Build läuft bereits | build_status abfragen bis running: false, dann erneut auslösen |
409 bei create_article | Live-Artikel mit diesem Slug existiert bereits | Für Änderungen update_article nutzen, sonst anderen Slug wählen |
409 bei create_section | Sektion existiert bereits oder Name ist reserviert | list_sections prüfen, anderen Namen wählen |
400 bei update_article (find/replace) | find-Anker 0-mal oder >1-mal im Body | Bei 0 Treffern den Anker per read_article gegen den echten Body prüfen; bei >1 mehr umgebenden Text — oder replace_all=true |
404 direkt nach create_article | Build noch nicht durch oder release_site vergessen | release_site auslösen, build_status abwarten; mit read_article statt öffentlicher URL prüfen |
404 bei read_article auf bekannten Artikel | Falscher Sektionspfad/Slug — oder Artikel liegt im Papierkorb | list_articles bzw. search_articles nutzen; list_trash prüfen |
| 401 konstant | Token abgelaufen, falsch oder falscher Host | Registrierung prüfen: claude mcp list muss ✓ Connected zeigen |
| 401/404 flackernd (mal ja, mal nein) | Auflösungsproblem auf Betreiberseite, kein Fehler Deines Aufrufs | Beim Betreiber melden — von aussen nicht behebbar |
payload_too_large bei upload_image | Base64-Inline-Upload über dem Limit | Foto per curl -F file=@… https://<cms-host>/api/v1/images hochladen |
| 302 auf einen Login beim Abruf einer öffentlichen URL | Die Instanz steht hinter Zugangsschutz — kein Fehler | Verifikation immer per read_article |
last_status: "error" in build_status | Build-Pipeline fehlgeschlagen | build_log lesen — meldet den fehlgeschlagenen Schritt strukturiert |
Artikel fehlt in search_articles, existiert aber | Suchindex kennt nur den Stand des letzten Builds | Erst release_site abschließen, dann suchen |
| Gantt-Diagramm zeigt ein Bomben-Icon, Build war grün | Feldliste, die Mermaid erst im Renderer übersetzt | Tag vor die ID setzen, höchstens drei Felder hinter dem : |
422 beim Speichern eines ```vegalite-Blocks | Kein gültiges JSON — oder die Spec zeigt auf entfernte Daten | Zahlen inline setzen; die Meldung nennt die Fundstelle im Spec-Baum |
| Build bricht ab mit „D2-/Vega-Lite-/DOT-Diagramm(e) mit Fehler" | Ein Block im Live-Inhalt lässt sich nicht rendern (im Papierkorb nicht blockierend) | build_log nennt Datei und Zeile |
| Diagramm sieht im Titelbild anders aus als im Artikel | Theme lädt mermaid@11 ungepinnt, der Renderer pinnt | Kein Fehler. Bei sichtbarem Unterschied das Titelbild mit force=true neu erzeugen |
Besonderheiten und Stolperfallen
delete_articleist Soft-Delete in den Papierkorb mit Epoch-Präfix (1714398123_slug.md). Die Kette:list_trashzeigt,restore_articlestellt wieder her,purge_articlelöscht endgültig — und verlangt dafürconfirm="purge".- Echte Fotos nie als Base64 über das MCP-Werkzeug hochladen. Stattdessen
curl -F file=@foto.jpg https://<cms-host>/api/v1/images. Das MCP-upload_imageist für kleine Grafiken gedacht; für generierte Diagramm-Titelbilder gibt es die fünf*_titelbild-Werkzeuge. - Sichtbarkeit unter
https://<cms-host>/<sektion>/<slug>/erst nachrelease_siteund Build-Ende. - Viele Instanzen stehen hinter einem Zugangsschutz — ein direkter Abruf der öffentlichen URL läuft dann in eine Sackgasse. Für Verifikation ohne Zugangsdaten immer
read_articlenutzen. - Eine Unterrubrik ist keine URL-Ebene. Die Permalink-Regel ist auf die oberste Sektion gekeyt: ein Artikel in
applebridge/atalkderscheint unter/applebridge/<slug>/. Ein Artikel und eine gleichnamige Unterrubrik verdrängen sich deshalb; beides übereinander anzulegen wird mit 409 abgelehnt.
Quellen
- list_tools der laufenden Instanz (massgebliches Werkzeug-Inventar)
- generic-cms-Repo: mcp/server.py, cms/blueprints/api.py, cms/blueprints/build.py
- Vorgaengerfassung (ABGELOEST am 2026-08-20, nicht mehr gepflegt): sealog.de/mcp/mcp.html, Erstfassung 2026-05-01