EmDash enthält einen integrierten Model Context Protocol (MCP)-Server unter /_emdash/api/mcp, der Content-Management-Operationen als Tools für KI-Assistenten bereitstellt.
Diese Seite behandelt die Protokolldetails: Authentifizierung, Transport, Tool-Spezifikationen, OAuth-Discovery und Fehlerbehandlung.
Authentifizierung
Der MCP-Server unterstützt drei Authentifizierungsmethoden:
| Methode | So funktioniert es |
|---|---|
| OAuth 2.1 Authorization Code + PKCE | Standardablauf für MCP-Clients. Der Benutzer genehmigt Berechtigungen im Browser. |
| Persönlicher Zugriffstoken (PAT) | Langlebige ec_pat_*-Tokens, die im Admin-Panel erstellt werden. |
| Device Flow | CLI-ähnlicher Ablauf, bei dem Sie einen Code im Browser bestätigen. Wird von emdash login verwendet. |
Sitzungscookies (aus der Admin-Oberfläche) funktionieren ebenfalls, sind aber für externe MCP-Clients nicht praktikabel.
Berechtigungen (Scopes)
Tokens sind auf bestimmte Berechtigungen beschränkt, um die möglichen Operationen eines Clients einzugrenzen. Berechtigungen werden während der OAuth-Autorisierung angefordert und bei jedem Tool-Aufruf durchgesetzt.
| Scope | Gewährt Zugriff auf |
|---|---|
content:read | Inhalte auflisten, abrufen, vergleichen und durchsuchen. Taxonomie-Begriffe und Menüs auflisten. |
content:write | Inhalte erstellen, aktualisieren, löschen, veröffentlichen, zurückziehen, planen, duplizieren und wiederherstellen. Taxonomie-Begriffe erstellen. |
media:read | Medienelemente auflisten und abrufen. |
media:write | Metadaten von Medien aktualisieren und löschen. |
schema:read | Sammlungen auflisten und Sammlungsschemata abrufen. |
schema:write | Sammlungen und Felder erstellen und löschen. |
admin | Vollständiger Zugriff auf alle Operationen. |
Der admin-Scope gewährt Zugriff auf alles. Sitzungsbasierte Authentifizierung (ohne Token) hat ebenfalls vollen Zugriff basierend auf der Rolle des Benutzers.
Rollenanforderungen
Zusätzlich zu den Berechtigungen erfordern einige Tools eine Mindest-RBAC-Rolle:
| Operation | Mindestrolle |
|---|---|
| Inhaltsoperationen | Keine Mindestanforderung (Berechtigungen steuern den Zugriff) |
| Schema lesen | Redakteur (40) |
| Schema schreiben | Administrator (50) |
Siehe die Authentifizierungsanleitung für Rollendefinitionen.
Transport
Der Server verwendet den Streamable-HTTP-Transport im zustandslosen Modus. Jede Anfrage ist unabhängig — es gibt keine Sitzungen oder langlebige Verbindungen.
POST /_emdash/api/mcp— JSON-RPC-Tool-Aufrufe sendenGET /_emdash/api/mcp— Gibt 405 zurück (kein SSE im zustandslosen Modus)DELETE /_emdash/api/mcp— Gibt 405 zurück (keine Sitzung zum Beenden)
Antworten folgen dem JSON-RPC 2.0-Format. Fehler verwenden Standard-JSON-RPC-Fehlercodes, mit MCP-spezifischen Codes für Berechtigungs- und Zugriffsfehler.
Tools
Der Server stellt 33 Tools in sieben Bereichen bereit. Jedes Tool gibt Ergebnisse als JSON-Textinhalt zurück oder eine Fehlermeldung mit isError: true bei einem Fehler.
Inhalts-Tools
content_list
Inhalte in einer Sammlung mit optionaler Filterung und Paginierung auflisten.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug (z. B. posts, pages) |
status | string | Nein | Filter: draft, published oder scheduled |
limit | integer | Nein | Maximale Anzahl zurückgegebener Elemente (1–100, Standard 50) |
cursor | string | Nein | Paginierungscursor aus einer vorherigen Antwort |
orderBy | string | Nein | Feld zum Sortieren (z. B. created_at, updated_at) |
order | string | Nein | Sortierrichtung: asc oder desc (Standard desc) |
locale | string | Nein | Nach Sprache filtern (z. B. en, fr). Nur relevant bei i18n. |
Scope: content:read | Nur Lesen: Ja
content_get
Ein einzelnes Inhaltselement anhand seiner ID oder seines Slugs abrufen. Gibt alle Feldwerte, Metadaten und ein _rev-Token für optimistische Nebenläufigkeit zurück.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID (ULID) oder Slug |
locale | string | Nein | Sprache für die Slug-Suche. IDs sind global eindeutig. |
Scope: content:read | Nur Lesen: Ja
content_create
Ein neues Inhaltselement erstellen. Das data-Objekt sollte Feldwerte enthalten, die dem Schema der Sammlung entsprechen — verwenden Sie schema_get_collection, um die verfügbaren Felder zu prüfen. Elemente werden standardmäßig als draft erstellt.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
data | object | Ja | Feldwerte als Schlüssel-Wert-Paare |
slug | string | Nein | URL-Slug (wird automatisch aus dem Titel generiert, wenn nicht angegeben) |
status | string | Nein | Anfangsstatus: draft oder published (Standard draft) |
locale | string | Nein | Sprache für diesen Inhalt (Standard: Website-Standard) |
translationOf | string | Nein | ID des Elements, dessen Übersetzung dies ist |
Scope: content:write
content_update
Ein bestehendes Inhaltselement aktualisieren. Geben Sie nur die Felder an, die Sie ändern möchten — nicht angegebene Felder bleiben unverändert.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
data | object | Nein | Zu aktualisierende Feldwerte |
slug | string | Nein | Neuer URL-Slug |
status | string | Nein | Neuer Status: draft oder published |
_rev | string | Nein | Revisionstoken von content_get zur Konflikterkennung |
Scope: content:write
content_delete
Ein Inhaltselement durch Verschieben in den Papierkorb vorläufig löschen. Verwenden Sie content_restore zum Rückgängigmachen oder content_permanent_delete zum endgültigen Entfernen.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
Scope: content:write | Destruktiv: Ja
content_restore
Ein vorläufig gelöschtes Inhaltselement aus dem Papierkorb wiederherstellen.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
Scope: content:write
content_permanent_delete
Ein Inhaltselement im Papierkorb dauerhaft und unwiderruflich löschen. Das Element muss sich zuvor im Papierkorb befinden.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
Scope: content:write | Destruktiv: Ja
content_publish
Ein Inhaltselement veröffentlichen und auf der Website live schalten. Erstellt eine veröffentlichte Revision aus dem aktuellen Entwurf. Weitere Bearbeitungen erzeugen einen neuen Entwurf, ohne die Live-Version zu beeinflussen, bis erneut veröffentlicht wird.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
Scope: content:write
content_unpublish
Ein veröffentlichtes Element in den Entwurfsstatus zurückversetzen. Es ist auf der Live-Website nicht mehr sichtbar, aber der Inhalt bleibt erhalten.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
Scope: content:write
content_schedule
Ein Inhaltselement für eine zukünftige Veröffentlichung planen. Es wird automatisch zum angegebenen Datum/Zeitpunkt veröffentlicht.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
scheduledAt | string | Ja | ISO 8601-Datum/Uhrzeit (z. B. 2026-06-01T09:00:00Z) |
Scope: content:write
content_compare
Die veröffentlichte (Live-)Version eines Inhaltselements mit seinem aktuellen Entwurf vergleichen. Gibt beide Versionen und ein Flag zurück, das anzeigt, ob Änderungen vorliegen.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
Scope: content:read | Nur Lesen: Ja
content_discard_draft
Den aktuellen Entwurf verwerfen und zur letzten veröffentlichten Version zurückkehren. Funktioniert nur bei Elementen, die mindestens einmal veröffentlicht wurden.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
Scope: content:write | Destruktiv: Ja
content_list_trashed
Vorläufig gelöschte Inhaltselemente im Papierkorb einer Sammlung auflisten.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
limit | integer | Nein | Maximale Elemente (1–100, Standard 50) |
cursor | string | Nein | Paginierungscursor |
Scope: content:read | Nur Lesen: Ja
content_duplicate
Eine Kopie eines bestehenden Inhaltselements erstellen. Das Duplikat wird als Entwurf erstellt, mit „(Kopie)” am Titel angehängt und einem automatisch generierten Slug.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug zum Duplizieren |
Scope: content:write
content_translations
Alle Sprachvarianten eines Inhaltselements abrufen. Gibt die Übersetzungsgruppe und eine Zusammenfassung jeder Sprachversion zurück. Nur relevant, wenn i18n aktiviert ist.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
Scope: content:read | Nur Lesen: Ja
Schema-Tools
schema_list_collections
Alle im CMS definierten Inhaltssammlungen auflisten. Gibt Slug, Label, unterstützte Funktionen und Zeitstempel zurück.
Keine Parameter.
Scope: schema:read | Mindestrolle: Redakteur | Nur Lesen: Ja
schema_get_collection
Detaillierte Informationen über eine Sammlung einschließlich aller Felddefinitionen abrufen. Felder beschreiben das Datenmodell: Name, Typ, Einschränkungen und Validierungsregeln. Verwenden Sie dies, um zu verstehen, was content_create und content_update erwarten.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
slug | string | Ja | Sammlungs-Slug (z. B. posts) |
Scope: schema:read | Mindestrolle: Redakteur | Nur Lesen: Ja
schema_create_collection
Eine neue Inhaltssammlung erstellen. Dies erstellt eine Datenbanktabelle und Schemadefinition. Der Slug muss aus Kleinbuchstaben, Ziffern und Unterstrichen bestehen und mit einem Buchstaben beginnen.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
slug | string | Ja | Eindeutiger Bezeichner (/^[a-z][a-z0-9_]*$/) |
label | string | Ja | Anzeigename (Plural, z. B. „Blog-Beiträge”) |
labelSingular | string | Nein | Anzeigename im Singular |
description | string | Nein | Beschreibung dieser Sammlung |
icon | string | Nein | Icon-Name für die Admin-Oberfläche |
supports | string[] | Nein | Funktionen: drafts, revisions, preview, scheduling, search (Standard: ['drafts', 'revisions']) |
Scope: schema:write | Mindestrolle: Administrator
schema_delete_collection
Eine Sammlung und ihre Datenbanktabelle löschen. Dies ist unwiderruflich und löscht alle Inhalte in der Sammlung.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
slug | string | Ja | Zu löschender Sammlungs-Slug |
force | boolean | Nein | Löschung erzwingen, auch wenn die Sammlung Inhalte enthält |
Scope: schema:write | Mindestrolle: Administrator | Destruktiv: Ja
schema_create_field
Ein neues Feld zum Schema einer Sammlung hinzufügen. Dies fügt der Datenbanktabelle eine Spalte hinzu.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
slug | string | Ja | Feldbezeichner (/^[a-z][a-z0-9_]*$/) |
label | string | Ja | Anzeigename |
type | string | Ja | Datentyp (siehe unten) |
required | boolean | Nein | Ob das Feld erforderlich ist |
unique | boolean | Nein | Ob Werte eindeutig sein müssen |
defaultValue | any | Nein | Standardwert für neue Elemente |
validation | object | Nein | Einschränkungen: min, max, minLength, maxLength, pattern, options |
options | object | Nein | Widget-Konfiguration: collection (für Referenzen), rows (für Textbereich) |
searchable | boolean | Nein | In den Volltextsuchindex aufnehmen |
translatable | boolean | Nein | Ob dieses Feld übersetzbar ist (Standard: true) |
Feldtypen: string, text, number, integer, boolean, datetime, select, multiSelect, portableText, image, file, reference, json, slug.
Für die Typen select und multiSelect geben Sie die erlaubten Werte in validation.options an.
Scope: schema:write | Mindestrolle: Administrator
schema_delete_field
Ein Feld aus einer Sammlung entfernen. Dies entfernt die Spalte und löscht alle Daten in diesem Feld. Unwiderruflich.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
fieldSlug | string | Ja | Zu entfernender Feld-Slug |
Scope: schema:write | Mindestrolle: Administrator | Destruktiv: Ja
Medien-Tools
media_list
Hochgeladene Mediendateien mit optionaler MIME-Typ-Filterung und Paginierung auflisten.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
mimeType | string | Nein | Nach MIME-Typ-Präfix filtern (z. B. image/, application/pdf) |
limit | integer | Nein | Maximale Elemente (1–100, Standard 50) |
cursor | string | Nein | Paginierungscursor |
Scope: media:read | Nur Lesen: Ja
media_get
Details einer einzelnen Mediendatei anhand der ID abrufen. Gibt Metadaten zurück, einschließlich Dateiname, MIME-Typ, Größe, Abmessungen, Alt-Text und URL.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
id | string | Ja | Medien-ID |
Scope: media:read | Nur Lesen: Ja
media_update
Metadaten einer hochgeladenen Mediendatei aktualisieren. Die Datei selbst kann nicht geändert werden.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
id | string | Ja | Medien-ID |
alt | string | Nein | Alt-Text für Barrierefreiheit |
caption | string | Nein | Bildunterschrift |
width | integer | Nein | Bildbreite in Pixeln |
height | integer | Nein | Bildhöhe in Pixeln |
Scope: media:write
media_delete
Eine Mediendatei dauerhaft löschen. Entfernt den Datenbankeintrag und die Datei aus dem Speicher. Inhalte, die auf dieses Medium verweisen, werden fehlerhafte Verweise haben.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
id | string | Ja | Medien-ID |
Scope: media:write | Destruktiv: Ja
Such-Tool
search
Volltextsuche über Inhaltssammlungen. Sammlungen müssen search in ihrer supports-Liste haben und Felder müssen als searchable markiert sein.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
query | string | Ja | Suchanfragetext |
collections | string[] | Nein | Suche auf bestimmte Sammlungs-Slugs beschränken |
locale | string | Nein | Ergebnisse nach Sprache filtern |
limit | integer | Nein | Maximale Ergebnisse (1–50, Standard 20) |
Scope: content:read | Nur Lesen: Ja
Taxonomie-Tools
taxonomy_list
Alle Taxonomie-Definitionen auflisten (z. B. Kategorien, Schlagwörter). Gibt Name, Label, ob hierarchisch, und zugehörige Sammlungen zurück.
Keine Parameter.
Scope: content:read | Nur Lesen: Ja
taxonomy_list_terms
Begriffe in einer Taxonomie mit Paginierung auflisten.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
taxonomy | string | Ja | Taxonomie-Name (z. B. categories, tags) |
limit | integer | Nein | Maximale Elemente (1–100, Standard 50) |
cursor | string | Nein | Paginierungscursor |
Scope: content:read | Nur Lesen: Ja
taxonomy_create_term
Einen neuen Begriff in einer Taxonomie erstellen. Für hierarchische Taxonomien geben Sie eine parentId an, um einen untergeordneten Begriff zu erstellen.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
taxonomy | string | Ja | Taxonomie-Name |
slug | string | Ja | URL-sicherer Bezeichner |
label | string | Ja | Anzeigename |
parentId | string | Nein | Übergeordnete Begriff-ID (für hierarchische Taxonomien) |
description | string | Nein | Beschreibung des Begriffs |
Scope: content:write
Menü-Tools
menu_list
Alle Navigationsmenüs auflisten. Gibt Name, Label und Zeitstempel zurück.
Keine Parameter.
Scope: content:read | Nur Lesen: Ja
menu_get
Ein Menü anhand des Namens einschließlich aller Einträge in Reihenfolge abrufen. Einträge haben ein Label, eine URL, einen Typ und optional einen übergeordneten Eintrag für Verschachtelung.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
name | string | Ja | Menüname (z. B. main, footer) |
Scope: content:read | Nur Lesen: Ja
Revisions-Tools
revision_list
Revisionsverlauf für ein Inhaltselement auflisten, neueste zuerst. Die Sammlung muss revisions unterstützen.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
collection | string | Ja | Sammlungs-Slug |
id | string | Ja | Inhaltselement-ID oder Slug |
limit | integer | Nein | Maximale Revisionen (1–50, Standard 20) |
Scope: content:read | Nur Lesen: Ja
revision_restore
Ein Inhaltselement auf eine frühere Revision zurücksetzen. Ersetzt den aktuellen Entwurf durch die Daten der angegebenen Revision. Wird nicht automatisch veröffentlicht — verwenden Sie anschließend content_publish, falls erforderlich.
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
revisionId | string | Ja | Wiederherzustellende Revisions-ID |
Scope: content:write
OAuth-Discovery
MCP-Clients, die OAuth 2.1 unterstützen, können automatisch erkennen, wie die Authentifizierung erfolgt. Der Server veröffentlicht zwei Metadaten-Dokumente:
Metadaten der geschützten Ressource
GET /.well-known/oauth-protected-resource
{
"resource": "https://example.com/_emdash/api/mcp",
"authorization_servers": ["https://example.com/_emdash"],
"scopes_supported": [
"content:read", "content:write",
"media:read", "media:write",
"schema:read", "schema:write",
"admin"
],
"bearer_methods_supported": ["header"]
}
Metadaten des Autorisierungsservers
GET /_emdash/.well-known/oauth-authorization-server
{
"issuer": "https://example.com/_emdash",
"authorization_endpoint": "https://example.com/_emdash/oauth/authorize",
"token_endpoint": "https://example.com/_emdash/api/oauth/token",
"scopes_supported": ["content:read", "content:write", "..."],
"response_types_supported": ["code"],
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code"
],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"],
"device_authorization_endpoint": "https://example.com/_emdash/api/oauth/device/code"
}
Wenn eine nicht authentifizierte Anfrage den MCP-Endpunkt erreicht, gibt der Server Folgendes zurück:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"
Dies löst den standardmäßigen MCP-Client-Discovery-Ablauf aus.
Fehlerbehandlung
Tool-Fehler werden als Textinhalt mit isError: true zurückgegeben:
{
"content": [{ "type": "text", "text": "Collection 'nonexistent' not found" }],
"isError": true
}
Berechtigungs- und Zugriffsfehler lösen MCP-Protokollfehler aus:
{
"jsonrpc": "2.0",
"error": {
"code": -32600,
"message": "Insufficient scope: requires content:write"
},
"id": 1
}
Transportebene-Fehler (Serverkonfigurationsfehler, unbehandelte Ausnahmen) geben den JSON-RPC-Fehlercode -32603 (Interner Fehler) zurück, ohne Implementierungsdetails preiszugeben.