EmDash espone un server Model Context Protocol (MCP) integrato su /_emdash/api/mcp. I client MCP lo usano per leggere e gestire contenuti, byline, schemi, media, tassonomie, menu, revisioni e impostazioni, e per esportare o importare l’intero sito.
Autenticazione
L’endpoint MCP richiede un token Bearer. EmDash supporta questi flussi di token:
| Metodo | Uso |
|---|---|
| OAuth 2.1 Authorization Code with Proof Key for Code Exchange (PKCE) | Client MCP interattivi. L’utente approva gli scope richiesti nel browser. |
| Personal access token | Accesso a lungo termine per un client o automazione. I token usano il prefisso ec_pat_ e si creano nell’admin. |
| OAuth 2.0 Device Authorization Grant | Client da riga di comando che chiedono all’utente di approvare un codice nel browser. emdash login usa questo flusso. |
I cookie di sessione non autenticano l’endpoint MCP.
Scopes
I token OAuth e di accesso personale limitano quali strumenti un client può chiamare. Il ruolo dell’utente viene verificato separatamente: uno scope non concede mai un permesso che l’utente non ha.
| Scope | Accesso |
|---|---|
content:read | Leggere e cercare contenuti, byline, tassonomie, termini, menu e revisioni. I contenuti tipo bozza richiedono anche il permesso content:read_drafts dell’utente. |
content:write | Creare e modificare contenuti, byline e revisioni. Concede anche taxonomies:manage e menus:manage per compatibilità con token esistenti. |
media:read | Leggere record media. |
media:write | Caricare, registrare, aggiornare ed eliminare media. |
schema:read | Leggere collection e campi. |
schema:write | Creare, aggiornare ed eliminare collection e campi. |
taxonomies:manage | Creare, aggiornare ed eliminare definizioni di tassonomia e termini. |
menus:manage | Creare, aggiornare ed eliminare menu e voci di menu. |
settings:read | Leggere le impostazioni del sito. |
settings:manage | Aggiornare le impostazioni del sito. |
mcp:tools | Chiamare strumenti MCP esposti da qualsiasi plugin abilitato. |
mcp:tools:<pluginId> | Chiamare strumenti MCP esposti da un plugin abilitato. |
transfer:export | Esportare l’intero sito come pacchetto sito e scaricarlo. |
transfer:analyze | Caricare un pacchetto sito e analizzarlo per l’import. |
transfer:execute | Avviare, far avanzare, annullare e abbandonare un import di sito. |
admin | Chiamare ogni strumento core, inclusi quelli di trasferimento sito. Gli strumenti plugin richiedono ancora mcp:tools o lo scope specifico del plugin. |
Lo scope admin include transfer:export, transfer:analyze e transfer:execute. Ogni scope di trasferimento concede solo le proprie azioni e richiede il ruolo amministratore. Per consentire a un client, ad esempio un agente, di analizzare un pacchetto senza esportare o importare, concedete transfer:analyze invece di admin.
La pagina di consenso del codice di autorizzazione consente all’utente di rimuovere scope richiesti. EmDash interseca anche la richiesta con gli scope registrati del client e il ruolo dell’utente e rifiuta una concessione vuota.
Requisiti di ruolo
La tabella seguente mostra il ruolo minimo per l’ampia capacità. I controlli di proprietà possono richiedere un ruolo superiore quando un utente agisce su contenuti di un altro utente.
| Capacità | Ruolo minimo |
|---|---|
| Leggere contenuti pubblicati, media, tassonomie, termini e menu | Subscriber |
| Leggere bozze, contenuti programmati, cestino, confronti e revisioni | Contributor |
| Creare contenuti o caricare media | Contributor |
| Modificare o pubblicare contenuti propri e registrare media | Author |
| Gestire byline, tassonomie, menu o contenuti di tutti gli utenti | Editor |
| Leggere schemi o impostazioni | Editor |
| Modificare schemi o impostazioni, eliminare definitivamente contenuti o riparare l’uso dei media | Admin |
| Esportare o importare l’intero sito | Admin |
Vedere ruoli utente per le definizioni complete.
Trasporto
Il server usa HTTP Streamable senza stato. Ogni richiesta è indipendente; il server non mantiene una sessione MCP né una connessione Server-Sent Events.
| Metodo | Endpoint | Comportamento |
|---|---|---|
POST | /_emdash/api/mcp | Accetta inizializzazione JSON-RPC, elenco strumenti e chiamate strumento. |
GET | /_emdash/api/mcp | Restituisce 405 Method Not Allowed. |
DELETE | /_emdash/api/mcp | Restituisce 405 Method Not Allowed. |
Le risposte usano JSON-RPC 2.0. Chiamate tools/list per ottenere gli schemi di input e le annotazioni MCP attuali prima di costruire una richiesta strumento.
Inventario strumenti
L’inventario seguente corrisponde agli strumenti statici restituiti da tools/list. Il titolo registrato è incluso perché i client possono mostrarlo al posto del nome strumento.
Strumenti contenuto
| Tool | Registered title | Required scope |
|---|---|---|
content_list | List Content | content:read |
content_get | Get Content | content:read |
content_create | Create Content | content:write |
content_update | Update Content | content:write |
content_delete | Delete Content (Trash) | content:write |
content_restore | Restore Content | content:write |
content_permanent_delete | Permanently Delete Content | content:write |
content_publish | Publish Content | content:write |
content_unpublish | Unpublish Content | content:write |
content_schedule | Schedule Content | content:write |
content_unschedule | Cancel Scheduled Publication | content:write |
content_compare | Compare Live vs Draft | content:read |
content_discard_draft | Discard Draft | content:write |
content_list_trashed | List Trashed Content | content:read |
content_duplicate | Duplicate Content | content:write |
content_translations | Get Content Translations | content:read |
Strumenti byline
| Tool | Registered title | Required scope |
|---|---|---|
byline_list | List Bylines | content:read |
byline_get | Get Byline | content:read |
byline_create | Create Byline | content:write |
byline_update | Update Byline | content:write |
byline_delete | Delete Byline | content:write |
byline_translations | List Byline Translations | content:read |
Strumenti schema
| Tool | Registered title | Required scope |
|---|---|---|
schema_list_collections | List Collections | schema:read |
schema_get_collection | Get Collection Schema | schema:read |
schema_list_block_types | List Block Types | schema:read |
schema_get_block_type | Get Block Type | schema:read |
schema_create_block_type | Create Block Type | schema:write |
schema_update_block_type | Update Block Type | schema:write |
schema_activate_block_type_version | Activate Block Type Version | schema:write |
schema_create_collection | Create Collection | schema:write |
schema_delete_collection | Delete Collection | schema:write |
schema_update_collection | Update Collection | schema:write |
schema_create_field | Add Field to Collection | schema:write |
schema_delete_field | Remove Field from Collection | schema:write |
schema_update_field | Update Field | schema:write |
Strumenti media
| Tool | Registered title | Required scope |
|---|---|---|
media_list | List Media | media:read |
media_create | Confirm Signed Media Upload | media:write |
media_upload | Upload Media | media:write |
media_get | Get Media Item | media:read |
media_update | Update Media Metadata | media:write |
media_delete | Delete Media | media:write |
media_usage_repair | Repair Media Usage Index | admin |
Strumento di ricerca
| Tool | Registered title | Required scope |
|---|---|---|
search | Search Content | content:read |
Strumenti tassonomia
| Tool | Registered title | Required scope |
|---|---|---|
taxonomy_list | List Taxonomies | content:read |
taxonomy_get | Get Taxonomy Definition | content:read |
taxonomy_create | Create Taxonomy Definition | taxonomies:manage |
taxonomy_update | Update Taxonomy Definition | taxonomies:manage |
taxonomy_delete | Delete Taxonomy Definition | taxonomies:manage |
taxonomy_list_terms | List Taxonomy Terms | content:read |
taxonomy_create_term | Create Taxonomy Term | taxonomies:manage |
taxonomy_update_term | Update Taxonomy Term | taxonomies:manage |
taxonomy_delete_term | Delete Taxonomy Term | taxonomies:manage |
taxonomy_term_translations | List Term Translations | content:read |
Strumenti menu
| Tool | Registered title | Required scope |
|---|---|---|
menu_list | List Menus | content:read |
menu_get | Get Menu with Items | content:read |
menu_translations | List Menu Translations | content:read |
menu_create | Create Menu | menus:manage |
menu_update | Update Menu | menus:manage |
menu_delete | Delete Menu | menus:manage |
menu_set_items | Set Menu Items | menus:manage |
Strumenti revisione
| Tool | Registered title | Required scope |
|---|---|---|
revision_list | List Revisions | content:read |
revision_restore | Restore Revision | content:write |
Strumenti impostazioni
| Tool | Registered title | Required scope |
|---|---|---|
settings_get | Get Site Settings | settings:read |
settings_update | Update Site Settings | settings:manage |
Strumenti trasferimento sito
transfer:* indica uno tra transfer:export, transfer:analyze o transfer:execute. Lo scope admin soddisfa ogni requisito in questa tabella.
| Tool | Registered title | Required scope |
|---|---|---|
site_transfer_capabilities | Get Site Transfer Capabilities | transfer:* |
site_export_start | Start Site Export | transfer:export |
site_export_status | Get Site Export Status | transfer:export |
site_import_analyze | Analyze Site Import | transfer:analyze |
site_import_start | Start Site Import | transfer:execute |
site_import_status | Get Site Import Status | transfer:* |
site_import_resume | Resume Site Import | transfer:execute |
site_import_receipt | Get Site Import Receipt | transfer:* |
site_export_start e site_import_start accettano anche un token senza lo scope quando un admin approva la richiesta. Per l’operazione avviata da una richiesta approvata, site_export_status, site_import_status, site_import_resume e site_import_receipt accettano lo stesso token senza lo scope.
Usare gli schemi strumento
tools/list restituisce per ogni strumento descrizione, schema di input JSON e annotazioni. Leggete tali metadati prima di costruire una chiamata così il client usa campi, valori consentiti e limiti supportati dalla versione EmDash installata.
Ad esempio, un client che aggiorna un articolo chiama prima content_get e conserva il _rev restituito. Può poi inviare questa richiesta JSON-RPC:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "content_update",
"arguments": {
"collection": "articles",
"id": "01JARTICLE0000000000000000",
"data": { "title": "Updated title" },
"_rev": "opaque-revision-token"
}
}
}
Il risultato è restituito come testo JSON nel primo blocco contenuto. Uno strumento con schema di output può restituire lo stesso valore anche in structuredContent.
Ciclo di vita contenuti e byline
Il riferimento al ciclo di vita dei contenuti definisce stato, revisione, permessi, conflitti e comportamento degli hook condivisi da MCP, REST, CLI e pannello admin.
content_get restituisce un valore _rev opaco. Passatelo a content_update, content_publish, content_unpublish, content_schedule o content_discard_draft. Un valore obsoleto restituisce un conflitto; rileggete l’elemento prima di riprovare.
content_update è un aggiornamento parziale: i campi omessi mantengono i valori attuali. Aggiornare un elemento pubblicato prepara una bozza mentre la versione live resta invariata. Usate content_compare per rivedere valori live e bozza, poi chiamate content_publish per rendere live la bozza o content_discard_draft per rimuoverla. content_delete sposta un elemento nel cestino; solo content_permanent_delete rimuove definitivamente un elemento nel cestino.
Le byline sono crediti riutilizzabili di autore o collaboratore. byline_create può creare un credito ospite o collegare una byline a un utente CMS. Passate l’ID byline restituito nell’input bylines accettato da content_create e content_update. Eliminare una byline rimuove quel credito dai contenuti e lo cancella come byline principale.
Le scritture MCP non partecipano al blocco modifica voce dell’admin. Il controllo _rev protegge le operazioni che lo accettano, ma altri strumenti di scrittura possono modificare una voce mentre un editor la tiene aperta.
Traduzioni
Gli strumenti di traduzione di contenuti, byline, termini di tassonomia e menu restituiscono ogni variante locale nel gruppo di traduzione pertinente. Usate l’input translationOf dello strumento di creazione quando lo schema lo prevede; tools/list è autorevole per i campi richiesti.
content_translations accetta collection e ID o slug del contenuto. Gli strumenti di traduzione byline, termine e menu accettano l’ID di un record o l’ID condiviso del gruppo di traduzione. Un utente senza accesso alle bozze vede solo traduzioni di contenuti pubblicati.
Schemi, media, tassonomie e menu
Gli strumenti schema modificano la struttura del database. Usate schema_get_collection prima di creare contenuti o modificare campi; restituisce nomi campo, tipi, vincoli e regole di validazione disponibili. L’eliminazione di collection e campi rimuove contenuti memorizzati o valori di campo e non può essere annullata.
Usate media_upload per inviare byte codificati in base64. I caricamenti sono soggetti ai limiti di dimensione e tipo MIME configurati; byte identici possono restituire un elemento media esistente con deduplicated: true.
media_create conferma un caricamento in sospeso creato tramite POST /_emdash/api/media/upload-url. Caricate il file con l’URL firmata restituita, poi chiamate media_create dallo stesso account utente con il storageKey restituito. Lo strumento verifica che il file memorizzato esista e corrisponda alla dimensione indicata quando è stata richiesta l’URL di caricamento prima di renderlo disponibile nella libreria media.
Le definizioni di tassonomia descrivono la classificazione e le collection a cui si applica; i termini sono i valori individuali assegnati ai contenuti. I termini gerarchici possono usare parentId, ma un genitore deve appartenere alla stessa tassonomia e non può creare un ciclo. Creare o aggiornare un termine con parentId in una tassonomia non gerarchica restituisce VALIDATION_ERROR. Un termine con figli deve averli rimossi o spostati prima dell’eliminazione.
menu_set_items sostituisce l’elenco completo delle voci di un menu in un’operazione atomica. L’ordine dell’array diventa l’ordine del menu. Il parentIndex di una voce annidata punta a una voce precedente nello stesso array; posizionate ogni genitore prima dei figli.
media_usage_repair può elaborare una collection o tutte le collection e può richiedere molto tempo su un sito grande. Gli stati complete, partial, failed e stale sono risposte strumento riuscite. Ispezionate stato e conteggi restituiti invece di affidarvi a isError; autenticazione, validazione e errori di esecuzione imprevisti impostano isError: true.
Trasferimento sito
Gli strumenti site_* esportano un intero sito come pacchetto sito e importano un pacchetto in un sito vuoto. Avviano e guidano operazioni e restituiscono riepiloghi limitati. Non trasportano mai byte del pacchetto, media, contenuti dei record, indirizzi email dei principal né URL di download. Scaricate un export e caricate un pacchetto per l’import con la CLI o l’API REST, poi riferite l’operazione per ID.
Ogni strumento di trasferimento richiede il ruolo Admin. Il ruolo è verificato prima dello scope; un chiamante non admin riceve INSUFFICIENT_PERMISSIONS e non viene creata alcuna richiesta di approvazione.
Export
site_export_start accetta comments (predefinito true) e restituisce la nuova operazione. site_export_status esegue un passo di export limitato a ogni chiamata e riporta l’operazione e nextRequestInMs. Richiamate dopo quel ritardo finché nextRequestInMs non è null. Passate advance: false per leggere lo stato senza eseguire un passo. Al completamento dell’export, il risultato include anche totals: conteggi record per tipo, conteggio e byte media, e conteggio e byte file del pacchetto.
Import
Caricate prima il pacchetto. emdash site import <file> --analyze della CLI carica, analizza e stampa l’ID operazione.
site_import_analyze esegue un passo di analisi limitato per chiamata. Ripetete finché nextRequestInMs non è null; il risultato include allora un riepilogo piano con packageDigest, planDigest, executable, conteggi, dimensioni, principal, decisioni, trasformazioni, avvisi e blocker. Ogni trasformazione è elencata come code, kind del record se presente, e count, senza ID o valori a cui si applica. Principal, avvisi e blocker elencano al massimo 50 elementi ciascuno, con il conteggio totale in total. I principal sono elencati senza indirizzi email, con ID utente destinazione suggeriti e mappati attualmente. Passate decisions per mappare i principal agli ID utente destinazione (o null) e scegliere titolo e tagline del pacchetto o destinazione. Ogni modifica produce un nuovo planDigest.
site_import_start prende l’ID operazione e i packageDigest e planDigest dell’ultimo piano. Il piano non deve avere blocker. Lo strumento ha destructiveHint: true: una volta avviato, l’import scrive sul sito e blocca altre scritture finché non completa o un amministratore lo abbandona. Mostrate il piano all’utente e ottenete conferma prima di chiamarlo.
site_import_resume esegue un passo di import limitato e riporta operazione e nextRequestInMs. Chiamatelo finché nextRequestInMs non è null; è sicuro ripetere dopo una disconnessione. site_import_status riporta operazione e conteggi file caricati senza far avanzare l’import. site_import_receipt restituisce la ricevuta completa, incluso receiptDigest, al completamento dell’import.
Mentre un import è in esecuzione, e dopo fallimento o annullamento finché non viene abbandonato, ogni altro strumento che può scrivere fallisce con TRANSFER_IMPORT_IN_PROGRESS. Include gli strumenti plugin. Gli strumenti annotati readOnlyHint: true e gli otto strumenti site_* continuano a funzionare, e initialize e tools/list non sono mai bloccati. Durante l’attivazione uso media, gli strumenti di scrittura falliscono allo stesso modo con MEDIA_USAGE_ACTIVATION_IN_PROGRESS.
I riepiloghi operazione includono id, kind, state, stage, progress, packageDigest, planDigest, error ({ code } o null) e timestamp. progress è { done, total } passi, più records scritti finora da un export, e bytesDone e bytesTotal quando noti. Gli strumenti MCP non possono annullare o abbandonare un import; usate l’API REST.
Approvazioni
Un token con admin o lo scope di trasferimento necessario non chiede mai approvazione. Per un token senza l’uno o l’altro, ad esempio un agente con solo transfer:analyze, site_export_start e site_import_start si eseguono quando un amministratore approva la richiesta:
- La prima chiamata senza scope crea una richiesta di approvazione in sospeso e fallisce con
TRANSFER_APPROVAL_REQUIRED. Testo del messaggio e_meta.detailsportanoapprovalIdeexpiresAt. Richiamare con gli stessi argomenti e senzaapprovalIdrestituisce la stessa richiesta aperta. - Un amministratore approva sotto Richieste di approvazione in Impostazioni → Transfer, o con l’endpoint di approvazione solo sessione dell’API REST. I token API non possono approvare richieste.
- Il client ripete la chiamata con gli stessi argomenti e l’
approvalId. L’approvazione si consuma quando quella chiamata avvia l’operazione. Se l’operazione non parte, il client può riprovare con lo stessoapprovalIdfinché non scade.
Una richiesta è legata a utente, token, azione e argomenti esatti: opzioni export, o ID operazione import e entrambi i digest. Una richiesta in sospeso scade 15 minuti dopo la creazione, una approvata 15 minuti dopo l’approvazione. Una chiamata con argomenti diversi o altro token, o con approvazione negata, scaduta o usata, fallisce con TRANSFER_APPROVAL_INVALID.
site_import_start verifica digest, stato operazione e blocker del piano prima di creare una richiesta, così un amministratore viene chiesto di approvare solo un import eseguibile. Un’approvazione richiede un ID token; un chiamante senza riceve INSUFFICIENT_SCOPE.
Dopo una chiamata approvata che avvia un’operazione, lo stesso utente e token possono chiamare site_export_status, o site_import_status, site_import_resume e site_import_receipt, per quell’operazione senza lo scope.
Strumenti plugin
Un amministratore deve abilitare la superficie MCP di ogni plugin. Gli strumenti abilitati compaiono in tools/list come <pluginId>__<localName> e richiedono mcp:tools o mcp:tools:<pluginId> per chiamate autenticate con token. EmDash verifica anche il permesso dichiarato dalla route plugin e registra plugin, strumento, route e attore nel log di audit.
Poiché gli strumenti plugin dipendono dall’installazione, non fanno parte dell’inventario statico sopra.
Scoperta OAuth
I client MCP scoprono il server di autorizzazione dai metadati della risorsa protetta:
GET /.well-known/oauth-protected-resource
La risposta identifica /_emdash/api/mcp come risorsa protetta e collega al server di autorizzazione. I client leggono poi i metadati a:
GET /.well-known/oauth-authorization-server/_emdash
Quel documento fornisce gli endpoint attuali di autorizzazione, token, registrazione e autorizzazione dispositivo, scope supportati, tipi di grant e il metodo PKCE S256. Usate i valori scoperti invece di codificare fisse le route del protocollo OAuth.
Una richiesta MCP non autenticata restituisce 401 con l’URL di scoperta:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"
Errori
Un fallimento strumento ha isError: true. Il primo blocco testo inizia con un codice stabile, e _meta.code lo ripete per client che leggono metadati strutturati:
{
"content": [{ "type": "text", "text": "[NOT_FOUND] Collection 'articles' not found" }],
"isError": true,
"_meta": { "code": "NOT_FOUND" }
}
I fallimenti di autenticazione usano codici come INSUFFICIENT_SCOPE e INSUFFICIENT_PERMISSIONS. I fallimenti di trasporto usano il codice errore interno JSON-RPC -32603 e non espongono l’eccezione sottostante.