EmDash include un server Model Context Protocol (MCP) integrato all’indirizzo /_emdash/api/mcp che espone le operazioni di gestione dei contenuti come strumenti per gli assistenti AI.
Questa pagina copre i dettagli del protocollo: autenticazione, trasporto, specifiche degli strumenti, discovery OAuth e gestione degli errori.
Autenticazione
Il server MCP supporta tre metodi di autenticazione:
| Metodo | Come funziona |
|---|---|
| OAuth 2.1 Authorization Code + PKCE | Flusso standard per i client MCP. L’utente approva gli scope nel browser. |
| Personal Access Token (PAT) | Token ec_pat_* a lunga durata creati nel pannello admin. |
| Device Flow | Flusso in stile CLI dove approvi un codice nel browser. Usato da emdash login. |
Anche i cookie di sessione (dall’interfaccia admin) funzionano, ma non sono pratici per client MCP esterni.
Scope
I token sono limitati da scope per controllare quali operazioni un client può eseguire. Gli scope vengono richiesti durante l’autorizzazione OAuth e applicati a ogni chiamata di strumento.
| Scope | Concede accesso a |
|---|---|
content:read | Elencare, ottenere, confrontare e cercare contenuti. Elencare termini di tassonomia e menu. |
content:write | Creare, aggiornare, eliminare, pubblicare, depubblicare, pianificare, duplicare e ripristinare contenuti. Creare termini di tassonomia. |
media:read | Elencare e ottenere elementi media. |
media:write | Aggiornare ed eliminare metadati dei media. |
schema:read | Elencare le collezioni e ottenere gli schemi delle collezioni. |
schema:write | Creare ed eliminare collezioni e campi. |
admin | Accesso completo a tutte le operazioni. |
Lo scope admin concede accesso a tutto. L’autenticazione basata su sessione (senza token) ha anche accesso completo in base al ruolo dell’utente.
Requisiti di ruolo
Oltre agli scope, alcuni strumenti richiedono un ruolo RBAC minimo:
| Operazione | Ruolo minimo |
|---|---|
| Operazioni sui contenuti | Nessun minimo (gli scope controllano l’accesso) |
| Lettura schema | Editor (40) |
| Scrittura schema | Admin (50) |
Vedi la guida all’autenticazione per le definizioni dei ruoli.
Trasporto
Il server utilizza il trasporto Streamable HTTP in modalità stateless. Ogni richiesta è indipendente — non ci sono sessioni o connessioni persistenti.
POST /_emdash/api/mcp— Invia chiamate di strumenti JSON-RPCGET /_emdash/api/mcp— Restituisce 405 (nessun SSE in modalità stateless)DELETE /_emdash/api/mcp— Restituisce 405 (nessuna sessione da chiudere)
Le risposte seguono il formato JSON-RPC 2.0. Gli errori usano codici di errore JSON-RPC standard, con codici specifici MCP per errori di scope e permessi.
Strumenti
Il server espone 33 strumenti in sette domini. Ogni strumento restituisce risultati come contenuto di testo JSON, o un messaggio di errore con isError: true in caso di fallimento.
Strumenti per i contenuti
content_list
Elenca gli elementi di contenuto in una collezione con filtraggio e paginazione opzionali.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione (es. posts, pages) |
status | string | No | Filtro: draft, published o scheduled |
limit | integer | No | Numero massimo di elementi (1-100, predefinito 50) |
cursor | string | No | Cursore di paginazione da una risposta precedente |
orderBy | string | No | Campo per l’ordinamento (es. created_at, updated_at) |
order | string | No | Direzione ordinamento: asc o desc (predefinito desc) |
locale | string | No | Filtra per locale (es. en, fr). Rilevante solo con i18n. |
Scope: content:read | Sola lettura: Sì
content_get
Ottieni un singolo elemento di contenuto per ID o slug. Restituisce tutti i valori dei campi, i metadati e un token _rev per la concorrenza ottimistica.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento (ULID) o slug |
locale | string | No | Locale per la ricerca per slug. Gli ID sono globalmente univoci. |
Scope: content:read | Sola lettura: Sì
content_create
Crea un nuovo elemento di contenuto. L’oggetto data deve contenere valori dei campi corrispondenti allo schema della collezione — usa schema_get_collection per verificare quali campi sono disponibili. Gli elementi vengono creati come draft per impostazione predefinita.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
data | object | Sì | Valori dei campi come coppie chiave-valore |
slug | string | No | Slug URL (generato automaticamente dal titolo se omesso) |
status | string | No | Stato iniziale: draft o published (predefinito draft) |
locale | string | No | Locale per questo contenuto (predefinito: default del sito) |
translationOf | string | No | ID dell’elemento di cui questo è una traduzione |
Scope: content:write
content_update
Aggiorna un elemento di contenuto esistente. Includi solo i campi che vuoi modificare — i campi non specificati rimangono invariati.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
data | object | No | Valori dei campi da aggiornare |
slug | string | No | Nuovo slug URL |
status | string | No | Nuovo stato: draft o published |
_rev | string | No | Token di revisione da content_get per il rilevamento dei conflitti |
Scope: content:write
content_delete
Elimina temporaneamente un elemento di contenuto spostandolo nel cestino. Usa content_restore per annullare, o content_permanent_delete per rimuoverlo definitivamente.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
Scope: content:write | Distruttivo: Sì
content_restore
Ripristina un elemento di contenuto eliminato temporaneamente dal cestino.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
Scope: content:write
content_permanent_delete
Elimina permanentemente e irreversibilmente un elemento nel cestino. L’elemento deve essere prima nel cestino.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
Scope: content:write | Distruttivo: Sì
content_publish
Pubblica un elemento di contenuto, rendendolo visibile sul sito. Crea una revisione pubblicata dalla bozza corrente. Le modifiche successive creano una nuova bozza senza influire sulla versione live fino alla ripubblicazione.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
Scope: content:write
content_unpublish
Riporta un elemento pubblicato allo stato di bozza. Non sarà più visibile sul sito live, ma il suo contenuto viene preservato.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
Scope: content:write
content_schedule
Pianifica un elemento di contenuto per la pubblicazione futura. Verrà pubblicato automaticamente alla data/ora specificata.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
scheduledAt | string | Sì | Data e ora ISO 8601 (es. 2026-06-01T09:00:00Z) |
Scope: content:write
content_compare
Confronta la versione pubblicata (live) di un elemento di contenuto con la sua bozza corrente. Restituisce entrambe le versioni e un flag che indica se ci sono modifiche.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
Scope: content:read | Sola lettura: Sì
content_discard_draft
Scarta la bozza corrente e ripristina l’ultima versione pubblicata. Funziona solo su elementi che sono stati pubblicati almeno una volta.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
Scope: content:write | Distruttivo: Sì
content_list_trashed
Elenca gli elementi di contenuto eliminati temporaneamente nel cestino di una collezione.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
limit | integer | No | Numero massimo (1-100, predefinito 50) |
cursor | string | No | Cursore di paginazione |
Scope: content:read | Sola lettura: Sì
content_duplicate
Crea una copia di un elemento di contenuto esistente. Il duplicato viene creato come bozza con “(Copy)” aggiunto al titolo e uno slug generato automaticamente.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug da duplicare |
Scope: content:write
content_translations
Ottieni tutte le varianti locali di un elemento di contenuto. Restituisce il gruppo di traduzione e un riepilogo di ogni versione locale. Rilevante solo quando l’i18n è abilitato.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
Scope: content:read | Sola lettura: Sì
Strumenti per lo schema
schema_list_collections
Elenca tutte le collezioni di contenuto definite nel CMS. Restituisce slug, etichetta, funzionalità supportate e timestamp.
Nessun parametro.
Scope: schema:read | Ruolo minimo: Editor | Sola lettura: Sì
schema_get_collection
Ottieni informazioni dettagliate su una collezione, incluse tutte le definizioni dei campi. I campi descrivono il modello dati: nome, tipo, vincoli e regole di validazione. Usalo per capire cosa si aspettano content_create e content_update.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
slug | string | Sì | Slug della collezione (es. posts) |
Scope: schema:read | Ruolo minimo: Editor | Sola lettura: Sì
schema_create_collection
Crea una nuova collezione di contenuto. Questo crea una tabella del database e una definizione dello schema. Lo slug deve essere alfanumerico minuscolo con underscore, che inizia con una lettera.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
slug | string | Sì | Identificatore univoco (/^[a-z][a-z0-9_]*$/) |
label | string | Sì | Nome visualizzato (plurale, es. “Blog Posts”) |
labelSingular | string | No | Nome visualizzato singolare |
description | string | No | Descrizione della collezione |
icon | string | No | Nome icona per l’interfaccia admin |
supports | string[] | No | Funzionalità: drafts, revisions, preview, scheduling, search (predefinito: ['drafts', 'revisions']) |
Scope: schema:write | Ruolo minimo: Admin
schema_delete_collection
Elimina una collezione e la sua tabella del database. Questo è irreversibile e elimina tutti i contenuti nella collezione.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
slug | string | Sì | Slug della collezione da eliminare |
force | boolean | No | Forza l’eliminazione anche se la collezione ha contenuti |
Scope: schema:write | Ruolo minimo: Admin | Distruttivo: Sì
schema_create_field
Aggiungi un nuovo campo allo schema di una collezione. Questo aggiunge una colonna alla tabella del database.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
slug | string | Sì | Identificatore del campo (/^[a-z][a-z0-9_]*$/) |
label | string | Sì | Nome visualizzato |
type | string | Sì | Tipo di dato (vedi sotto) |
required | boolean | No | Se il campo è obbligatorio |
unique | boolean | No | Se i valori devono essere univoci |
defaultValue | any | No | Valore predefinito per i nuovi elementi |
validation | object | No | Vincoli: min, max, minLength, maxLength, pattern, options |
options | object | No | Configurazione widget: collection (per riferimenti), rows (per textarea) |
searchable | boolean | No | Includi nell’indice di ricerca full-text |
translatable | boolean | No | Se questo campo è traducibile (predefinito true) |
Tipi di campo: string, text, number, integer, boolean, datetime, select, multiSelect, portableText, image, file, reference, json, slug.
Per i tipi select e multiSelect, fornisci i valori consentiti in validation.options.
Scope: schema:write | Ruolo minimo: Admin
schema_delete_field
Rimuovi un campo da una collezione. Questo elimina la colonna e tutti i dati in quel campo. Irreversibile.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
fieldSlug | string | Sì | Slug del campo da rimuovere |
Scope: schema:write | Ruolo minimo: Admin | Distruttivo: Sì
Strumenti per i media
media_list
Elenca i file media caricati con filtraggio opzionale per tipo MIME e paginazione.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
mimeType | string | No | Filtra per prefisso tipo MIME (es. image/, application/pdf) |
limit | integer | No | Numero massimo (1-100, predefinito 50) |
cursor | string | No | Cursore di paginazione |
Scope: media:read | Sola lettura: Sì
media_get
Ottieni i dettagli di un singolo file media per ID. Restituisce metadati inclusi nome file, tipo MIME, dimensione, dimensioni, testo alternativo e URL.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
id | string | Sì | ID dell’elemento media |
Scope: media:read | Sola lettura: Sì
media_update
Aggiorna i metadati di un file media caricato. Il file stesso non può essere modificato.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
id | string | Sì | ID dell’elemento media |
alt | string | No | Testo alternativo per l’accessibilità |
caption | string | No | Testo didascalia |
width | integer | No | Larghezza immagine in pixel |
height | integer | No | Altezza immagine in pixel |
Scope: media:write
media_delete
Elimina permanentemente un file media. Rimuove il record dal database e il file dallo storage. I contenuti che fanno riferimento a questo media avranno riferimenti interrotti.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
id | string | Sì | ID dell’elemento media |
Scope: media:write | Distruttivo: Sì
Strumento di ricerca
search
Ricerca full-text nelle collezioni di contenuto. Le collezioni devono avere search nella loro lista supports e i campi devono essere contrassegnati come searchable.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
query | string | Sì | Testo della query di ricerca |
collections | string[] | No | Limita la ricerca a slug di collezioni specifiche |
locale | string | No | Filtra i risultati per locale |
limit | integer | No | Risultati massimi (1-50, predefinito 20) |
Scope: content:read | Sola lettura: Sì
Strumenti per le tassonomie
taxonomy_list
Elenca tutte le definizioni delle tassonomie (es. categorie, tag). Restituisce nome, etichetta, se è gerarchica e le collezioni associate.
Nessun parametro.
Scope: content:read | Sola lettura: Sì
taxonomy_list_terms
Elenca i termini di una tassonomia con paginazione.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
taxonomy | string | Sì | Nome della tassonomia (es. categories, tags) |
limit | integer | No | Numero massimo (1-100, predefinito 50) |
cursor | string | No | Cursore di paginazione |
Scope: content:read | Sola lettura: Sì
taxonomy_create_term
Crea un nuovo termine in una tassonomia. Per le tassonomie gerarchiche, specifica un parentId per creare un termine figlio.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
taxonomy | string | Sì | Nome della tassonomia |
slug | string | Sì | Identificatore URL-safe |
label | string | Sì | Nome visualizzato |
parentId | string | No | ID del termine genitore (per tassonomie gerarchiche) |
description | string | No | Descrizione del termine |
Scope: content:write
Strumenti per i menu
menu_list
Elenca tutti i menu di navigazione. Restituisce nome, etichetta e timestamp.
Nessun parametro.
Scope: content:read | Sola lettura: Sì
menu_get
Ottieni un menu per nome, incluse tutte le sue voci in ordine. Le voci hanno un’etichetta, URL, tipo e genitore opzionale per l’annidamento.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
name | string | Sì | Nome del menu (es. main, footer) |
Scope: content:read | Sola lettura: Sì
Strumenti per le revisioni
revision_list
Elenca la cronologia delle revisioni per un elemento di contenuto, dalla più recente. Richiede che la collezione supporti revisions.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
collection | string | Sì | Slug della collezione |
id | string | Sì | ID dell’elemento o slug |
limit | integer | No | Revisioni massime (1-50, predefinito 20) |
Scope: content:read | Sola lettura: Sì
revision_restore
Ripristina un elemento di contenuto a una revisione precedente. Sostituisce la bozza corrente con i dati della revisione specificata. Non viene pubblicato automaticamente — usa content_publish successivamente se necessario.
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
revisionId | string | Sì | ID della revisione da ripristinare |
Scope: content:write
Discovery OAuth
I client MCP che supportano OAuth 2.1 possono scoprire automaticamente come autenticarsi. Il server pubblica due documenti di metadati:
Metadati della risorsa protetta
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"]
}
Metadati del server di autorizzazione
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"
}
Quando una richiesta non autenticata raggiunge l’endpoint MCP, il server restituisce:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"
Questo attiva il flusso standard di discovery del client MCP.
Gestione degli errori
Gli errori degli strumenti vengono restituiti come contenuto di testo con isError: true:
{
"content": [{ "type": "text", "text": "Collection 'nonexistent' not found" }],
"isError": true
}
Gli errori di scope e permessi generano errori del protocollo MCP:
{
"jsonrpc": "2.0",
"error": {
"code": -32600,
"message": "Insufficient scope: requires content:write"
},
"id": 1
}
Gli errori a livello di trasporto (configurazione errata del server, eccezioni non gestite) restituiscono il codice di errore JSON-RPC -32603 (Internal error) senza divulgare dettagli implementativi.