Référence du serveur MCP

Sur cette page

EmDash expose un serveur Model Context Protocol (MCP) intégré à l’adresse /_emdash/api/mcp. Les clients MCP l’utilisent pour lire et gérer le contenu, les bylines, les schémas, les médias, les taxonomies, les menus, les révisions et les paramètres, ainsi que pour exporter ou importer l’intégralité du site.

Authentification

Le point de terminaison MCP exige un jeton Bearer. EmDash prend en charge les flux de jetons suivants :

MéthodeUtilisation
OAuth 2.1 Authorization Code with Proof Key for Code Exchange (PKCE)Clients MCP interactifs. L’utilisateur approuve dans un navigateur les scopes demandés.
Personal access tokenAccès de longue durée pour un client ou une automatisation. Les jetons utilisent le préfixe ec_pat_ et sont créés dans l’admin.
OAuth 2.0 Device Authorization GrantClients en ligne de commande qui demandent à l’utilisateur d’approuver un code dans un navigateur. emdash login utilise ce flux.

Les cookies de session n’authentifient pas le point de terminaison MCP.

Scopes

Les jetons OAuth et les jetons d’accès personnels limitent les outils qu’un client peut appeler. Le rôle de l’utilisateur est vérifié séparément : un scope n’accorde donc jamais une permission que l’utilisateur n’a pas.

ScopeAccès
content:readLire et rechercher le contenu, les bylines, les taxonomies, les termes, les menus et les révisions. Le contenu de type brouillon exige aussi la permission content:read_drafts de l’utilisateur.
content:writeCréer et modifier le contenu, les bylines et les révisions. Il accorde aussi taxonomies:manage et menus:manage par compatibilité avec les jetons existants.
media:readLire les enregistrements de médias.
media:writeTéléverser, enregistrer, mettre à jour et supprimer des médias.
schema:readLire les collections et les champs.
schema:writeCréer, mettre à jour et supprimer des collections et des champs.
taxonomies:manageCréer, mettre à jour et supprimer des définitions de taxonomies et des termes.
menus:manageCréer, mettre à jour et supprimer des menus et des éléments de menu.
settings:readLire les paramètres du site.
settings:manageMettre à jour les paramètres du site.
mcp:toolsAppeler les outils MCP exposés par n’importe quel plugin activé.
mcp:tools:<pluginId>Appeler les outils MCP exposés par un plugin activé donné.
transfer:exportExporter l’intégralité du site sous forme de paquet de site et le télécharger.
transfer:analyzeTéléverser un paquet de site et l’analyser en vue de son importation.
transfer:executeDémarrer, faire avancer, annuler et abandonner une importation de site.
adminAppeler tous les outils du noyau, y compris les outils de transfert de site. Les outils de plugins exigent toujours mcp:tools ou le scope propre au plugin.

Le scope admin inclut transfer:export, transfer:analyze et transfer:execute. Chaque scope de transfert n’accorde que ses propres actions et exige le rôle d’administrateur. Pour permettre à un client, par exemple un agent, d’analyser un paquet de site sans l’exporter ni l’importer, accordez transfer:analyze plutôt que admin.

La page de consentement du code d’autorisation permet à l’utilisateur de retirer des scopes demandés. EmDash recoupe aussi la demande avec les scopes enregistrés du client et le rôle de l’utilisateur, et refuse une autorisation vide.

Exigences de rôle

Le tableau suivant indique le rôle minimal pour chaque capacité générale. Les vérifications de propriété peuvent exiger un rôle plus élevé lorsqu’un utilisateur agit sur le contenu d’un autre utilisateur.

CapacitéRôle minimal
Lire le contenu publié, les médias, les taxonomies, les termes et les menusSubscriber
Lire les brouillons, le contenu planifié, la corbeille, les comparaisons et les révisionsContributor
Créer du contenu ou téléverser des médiasContributor
Modifier ou publier son propre contenu et enregistrer des médiasAuthor
Gérer les bylines, les taxonomies, les menus ou le contenu de tous les utilisateursEditor
Lire les schémas ou les paramètresEditor
Modifier les schémas ou les paramètres, supprimer définitivement du contenu ou réparer l’utilisation des médiasAdmin
Exporter ou importer l’intégralité du siteAdmin

Consultez les rôles utilisateur pour les définitions complètes des rôles.

Transport

Le serveur utilise un HTTP Streamable sans état. Chaque requête est indépendante ; le serveur ne conserve ni session MCP ni connexion Server-Sent Events.

MéthodePoint de terminaisonComportement
POST/_emdash/api/mcpAccepte l’initialisation JSON-RPC, le listage des outils et les appels d’outils.
GET/_emdash/api/mcpRenvoie 405 Method Not Allowed.
DELETE/_emdash/api/mcpRenvoie 405 Method Not Allowed.

Les réponses utilisent JSON-RPC 2.0. Appelez tools/list pour obtenir les schémas d’entrée et les annotations MCP actuels avant de construire une requête d’outil.

Inventaire des outils

L’inventaire suivant correspond aux outils statiques renvoyés par tools/list. Le titre enregistré est indiqué, car les clients peuvent l’afficher à la place du nom de l’outil.

Outils de contenu

OutilTitre enregistréScope requis
content_listList Contentcontent:read
content_getGet Contentcontent:read
content_createCreate Contentcontent:write
content_updateUpdate Contentcontent:write
content_deleteDelete Content (Trash)content:write
content_restoreRestore Contentcontent:write
content_permanent_deletePermanently Delete Contentcontent:write
content_publishPublish Contentcontent:write
content_unpublishUnpublish Contentcontent:write
content_scheduleSchedule Contentcontent:write
content_unscheduleCancel Scheduled Publicationcontent:write
content_compareCompare Live vs Draftcontent:read
content_discard_draftDiscard Draftcontent:write
content_list_trashedList Trashed Contentcontent:read
content_duplicateDuplicate Contentcontent:write
content_translationsGet Content Translationscontent:read

Outils byline

OutilTitre enregistréScope requis
byline_listList Bylinescontent:read
byline_getGet Bylinecontent:read
byline_createCreate Bylinecontent:write
byline_updateUpdate Bylinecontent:write
byline_deleteDelete Bylinecontent:write
byline_translationsList Byline Translationscontent:read

Outils de schéma

OutilTitre enregistréScope requis
schema_list_collectionsList Collectionsschema:read
schema_get_collectionGet Collection Schemaschema:read
schema_list_block_typesList Block Typesschema:read
schema_get_block_typeGet Block Typeschema:read
schema_create_block_typeCreate Block Typeschema:write
schema_update_block_typeUpdate Block Typeschema:write
schema_activate_block_type_versionActivate Block Type Versionschema:write
schema_create_collectionCreate Collectionschema:write
schema_delete_collectionDelete Collectionschema:write
schema_update_collectionUpdate Collectionschema:write
schema_create_fieldAdd Field to Collectionschema:write
schema_delete_fieldRemove Field from Collectionschema:write
schema_update_fieldUpdate Fieldschema:write

Outils média

OutilTitre enregistréScope requis
media_listList Mediamedia:read
media_createConfirm Signed Media Uploadmedia:write
media_uploadUpload Mediamedia:write
media_getGet Media Itemmedia:read
media_updateUpdate Media Metadatamedia:write
media_deleteDelete Mediamedia:write
media_usage_repairRepair Media Usage Indexadmin

Outil de recherche

OutilTitre enregistréScope requis
searchSearch Contentcontent:read

Outils de taxonomie

OutilTitre enregistréScope requis
taxonomy_listList Taxonomiescontent:read
taxonomy_getGet Taxonomy Definitioncontent:read
taxonomy_createCreate Taxonomy Definitiontaxonomies:manage
taxonomy_updateUpdate Taxonomy Definitiontaxonomies:manage
taxonomy_deleteDelete Taxonomy Definitiontaxonomies:manage
taxonomy_list_termsList Taxonomy Termscontent:read
taxonomy_create_termCreate Taxonomy Termtaxonomies:manage
taxonomy_update_termUpdate Taxonomy Termtaxonomies:manage
taxonomy_delete_termDelete Taxonomy Termtaxonomies:manage
taxonomy_term_translationsList Term Translationscontent:read

Outils de menu

OutilTitre enregistréScope requis
menu_listList Menuscontent:read
menu_getGet Menu with Itemscontent:read
menu_translationsList Menu Translationscontent:read
menu_createCreate Menumenus:manage
menu_updateUpdate Menumenus:manage
menu_deleteDelete Menumenus:manage
menu_set_itemsSet Menu Itemsmenus:manage

Outils de révision

OutilTitre enregistréScope requis
revision_listList Revisionscontent:read
revision_restoreRestore Revisioncontent:write

Outils de paramètres

OutilTitre enregistréScope requis
settings_getGet Site Settingssettings:read
settings_updateUpdate Site Settingssettings:manage

Outils de transfert de site

transfer:* désigne l’un quelconque des scopes transfer:export, transfer:analyze ou transfer:execute. Le scope admin satisfait toutes les exigences de ce tableau.

OutilTitre enregistréScope requis
site_transfer_capabilitiesGet Site Transfer Capabilitiestransfer:*
site_export_startStart Site Exporttransfer:export
site_export_statusGet Site Export Statustransfer:export
site_import_analyzeAnalyze Site Importtransfer:analyze
site_import_startStart Site Importtransfer:execute
site_import_statusGet Site Import Statustransfer:*
site_import_resumeResume Site Importtransfer:execute
site_import_receiptGet Site Import Receipttransfer:*

site_export_start et site_import_start acceptent aussi un jeton sans le scope lorsqu’un administrateur approuve la demande. Pour l’opération que démarre une demande approuvée, site_export_status, site_import_status, site_import_resume et site_import_receipt acceptent alors le même jeton sans le scope.

Utiliser les schémas des outils

tools/list renvoie la description, le schéma d’entrée JSON et les annotations de chaque outil. Lisez ces métadonnées avant de construire un appel afin que votre client utilise les champs, les valeurs autorisées et les limites pris en charge par la version d’EmDash installée.

Par exemple, un client qui met à jour un article appelle d’abord content_get et conserve le _rev renvoyé. Il peut ensuite envoyer cette requête 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"
		}
	}
}

Le résultat est renvoyé sous forme de texte JSON dans le premier bloc de contenu. Un outil doté d’un schéma de sortie peut aussi renvoyer la même valeur dans structuredContent.

Cycle de vie du contenu et bylines

La référence du cycle de vie du contenu définit le comportement des états, des révisions, des permissions, des conflits et des hooks partagé par MCP, REST, la CLI et le panneau d’administration.

content_get renvoie une valeur _rev opaque. Transmettez-la à content_update, content_publish, content_unpublish, content_schedule ou content_discard_draft. Une valeur périmée renvoie un conflit : relisez donc l’élément avant de réessayer.

content_update est une mise à jour partielle : les champs omis conservent leurs valeurs actuelles. La mise à jour d’un élément publié prépare un brouillon tandis que la version en ligne reste inchangée. Utilisez content_compare pour examiner les valeurs en ligne et celles du brouillon, puis appelez content_publish pour publier le brouillon ou content_discard_draft pour le supprimer. content_delete déplace un élément dans la corbeille ; seul content_permanent_delete supprime définitivement un élément placé dans la corbeille.

Les bylines sont des mentions réutilisables d’auteur ou de contributeur. byline_create peut créer une mention d’invité ou lier une byline à un utilisateur du CMS. Un utilisateur ne peut avoir qu’une seule byline par locale. Une traduction créée avec translationOf conserve l’utilisateur de la byline source, sauf si vous transmettez userId ; transmettez null pour laisser la traduction sans lien. Transmettez l’ID de byline renvoyé dans l’entrée bylines acceptée par content_create et content_update. Supprimer une byline retire cette mention du contenu et la supprime en tant que byline principale.

Tant qu’un autre utilisateur détient le verrou d’édition d’une entrée parce qu’il l’a ouverte dans l’admin, content_update, content_delete, content_publish, content_unpublish, content_schedule, content_unschedule, content_discard_draft et revision_restore échouent avec ENTRY_LOCKED, et l’erreur désigne le détenteur. Relire l’élément ne lève pas le refus. Transmettez overrideLock: true pour écrire malgré tout.

Traductions

Les outils de traduction du contenu, des bylines, des termes de taxonomie et des menus renvoient chaque variante de locale du groupe de traduction concerné. Utilisez l’entrée translationOf de l’outil de création lorsque son schéma en propose une ; tools/list fait foi pour les champs obligatoires.

content_translations accepte une collection et un ID ou un slug de contenu. Les outils de traduction des bylines, des termes de taxonomie et des menus acceptent soit l’ID d’un enregistrement, soit l’ID partagé du groupe de traduction. Un utilisateur sans accès aux brouillons ne voit que les traductions du contenu publié.

Schémas, médias, taxonomies et menus

Les outils de schéma modifient la structure de la base de données. Utilisez schema_get_collection avant de créer du contenu ou de modifier des champs ; il renvoie les noms de champs, les types, les contraintes et les règles de validation disponibles. La suppression de collections et de champs retire le contenu ou les valeurs de champs stockés et ne peut pas être annulée.

Utilisez media_upload pour envoyer des octets encodés en base64. Les téléversements sont soumis aux limites de taille et de type MIME configurées, et des octets identiques peuvent renvoyer un élément de média existant avec deduplicated: true.

media_create confirme un téléversement en attente créé via POST /_emdash/api/media/upload-url. Téléversez le fichier avec l’URL signée renvoyée, puis appelez media_create depuis le même compte utilisateur avec le storageKey renvoyé. L’outil vérifie que le fichier stocké existe et correspond à la taille fournie lors de la demande de l’URL de téléversement, avant de le rendre disponible dans la médiathèque.

Les définitions de taxonomie décrivent la classification et les collections auxquelles elle s’applique ; les termes sont les valeurs individuelles attribuées au contenu. Les termes hiérarchiques peuvent utiliser parentId, mais le parent doit appartenir à la même taxonomie et ne peut pas créer de cycle. La création ou la mise à jour d’un terme avec parentId dans une taxonomie non hiérarchique renvoie VALIDATION_ERROR. Un terme qui a des enfants doit voir ces enfants supprimés ou déplacés avant d’être supprimé.

menu_set_items remplace la liste complète des éléments d’un menu en une seule opération atomique. L’ordre du tableau devient l’ordre du menu. Le parentIndex d’un élément imbriqué pointe vers un élément antérieur du même tableau ; placez donc chaque parent avant ses enfants.

media_usage_repair peut traiter une collection ou toutes les collections et peut s’exécuter longtemps sur un grand site. Ses statuts complete, partial, failed et stale sont des réponses d’outil réussies. Inspectez le statut et les compteurs renvoyés plutôt que de vous fier à isError ; les échecs d’authentification, de validation et d’exécution inattendus définissent isError: true.

Transfert de site

Les outils site_* exportent un site entier sous forme de paquet de site et importent un paquet dans un site vide. Ils démarrent et pilotent les opérations et renvoient des résumés bornés. Ils ne transportent jamais les octets du paquet, les médias, le contenu des enregistrements, les adresses e-mail des principals ni les URL de téléchargement. Téléchargez un export et téléversez un paquet pour l’import avec la CLI ou l’API REST, puis désignez l’opération par son ID.

Chaque outil de transfert exige le rôle Admin. Le rôle est vérifié avant le scope : un appelant qui n’est pas administrateur reçoit donc INSUFFICIENT_PERMISSIONS et aucune demande d’approbation n’est créée.

Export

site_export_start accepte comments (true par défaut) et renvoie la nouvelle opération. site_export_status exécute une étape d’export bornée à chaque appel et indique l’opération et nextRequestInMs. Rappelez-le après ce délai jusqu’à ce que nextRequestInMs vaille null. Transmettez advance: false pour lire le statut sans exécuter d’étape. Une fois l’export terminé, le résultat inclut aussi totals : nombre d’enregistrements par type, nombre et octets de médias, et nombre et octets de fichiers du paquet.

Import

Téléversez d’abord le paquet. La commande emdash site import <file> --analyze de la CLI le téléverse, l’analyse et affiche l’ID de l’opération.

site_import_analyze exécute une étape d’analyse bornée à chaque appel. Répétez-le jusqu’à ce que nextRequestInMs vaille null ; le résultat comprend alors un résumé du plan avec packageDigest, planDigest, executable, des compteurs, des tailles, des principals, des décisions, des transformations, des avertissements et des bloqueurs. Chaque transformation est indiquée par son code, le kind de l’enregistrement lorsqu’elle en a un, et un count, sans les IDs ni les valeurs auxquels elle s’applique. Les principals, les avertissements et les bloqueurs sont listés à raison de 50 éléments au plus chacun, le total complet figurant dans total. Les principals sont listés sans adresse e-mail, avec les IDs d’utilisateur cible suggérés et ceux actuellement associés. Transmettez decisions pour associer des principals à des IDs d’utilisateur cible (ou à null) et pour choisir le titre et le slogan du paquet ou de la cible. Chaque modification produit un nouveau planDigest.

site_import_start prend l’ID de l’opération ainsi que le packageDigest et le planDigest du dernier plan. Le plan ne doit comporter aucun bloqueur. L’outil possède destructiveHint: true : une fois démarré, l’import écrit dans le site et bloque les autres écritures jusqu’à ce qu’il soit terminé ou qu’un administrateur l’abandonne. Montrez le plan à l’utilisateur et obtenez sa confirmation avant de l’appeler.

site_import_resume exécute une étape d’import bornée et indique l’opération et nextRequestInMs. Appelez-le jusqu’à ce que nextRequestInMs vaille null ; il est sûr de le répéter après une déconnexion. site_import_status indique l’opération et le nombre de fichiers téléversés sans faire avancer l’import. site_import_receipt renvoie le reçu complet, y compris receiptDigest, une fois l’import terminé.

Pendant l’exécution d’un import, et après son échec ou son annulation jusqu’à son abandon, tout autre outil capable d’écrire échoue avec TRANSFER_IMPORT_IN_PROGRESS. Cela inclut les outils de plugins. Les outils annotés readOnlyHint: true et les huit outils site_* continuent de fonctionner, et initialize et tools/list ne sont jamais bloqués. Pendant l’activation de l’utilisation des médias, les outils d’écriture échouent de la même manière avec MEDIA_USAGE_ACTIVATION_IN_PROGRESS.

Les résumés d’opération comprennent id, kind, state, stage, progress, packageDigest, planDigest, error ({ code } ou null) et des horodatages. progress est un nombre d’étapes { done, total }, auquel s’ajoutent records, les enregistrements qu’un export a écrits jusque-là, ainsi que bytesDone et bytesTotal, une fois connus. Les outils MCP ne peuvent ni annuler ni abandonner un import ; utilisez l’API REST.

Approbations

Un jeton avec admin ou avec le scope de transfert requis ne demande jamais d’approbation. Pour un jeton sans l’un ni l’autre, par exemple celui d’un agent auquel seul transfer:analyze a été accordé, site_export_start et site_import_start s’exécutent lorsqu’un administrateur approuve la demande :

  1. Le premier appel sans le scope crée une demande d’approbation en attente et échoue avec TRANSFER_APPROVAL_REQUIRED. Le texte du message et _meta.details contiennent l’approvalId et son expiresAt. Un nouvel appel avec les mêmes arguments et sans approvalId renvoie la même demande ouverte.
  2. Un administrateur approuve la demande sous Approval requests dans Settings → Transfer, ou avec le point de terminaison d’approbation réservé à la session de l’API REST. Les jetons d’API ne peuvent pas approuver de demandes.
  3. Le client répète l’appel avec les mêmes arguments et l’approvalId. L’approbation est consommée lorsque cet appel démarre l’opération. Si l’opération ne parvient pas à démarrer, le client peut réessayer avec le même approvalId jusqu’à son expiration.

Une demande est liée à l’utilisateur, au jeton, à l’action et aux arguments exacts : les options d’export, ou l’ID de l’opération d’import et les deux digests. Une demande en attente expire 15 minutes après sa création, et une demande approuvée 15 minutes après son approbation. Un appel avec des arguments différents ou un autre jeton, ou avec une approbation refusée, expirée ou déjà utilisée, échoue avec TRANSFER_APPROVAL_INVALID.

site_import_start vérifie les digests, l’état de l’opération et les bloqueurs du plan avant de créer une demande, de sorte qu’un administrateur n’est sollicité que pour approuver un import qui peut s’exécuter. Une approbation nécessite un ID de jeton ; un appelant qui n’en a pas reçoit donc INSUFFICIENT_SCOPE.

Une fois qu’un appel approuvé a démarré une opération, le même utilisateur et le même jeton peuvent appeler site_export_status, ou site_import_status, site_import_resume et site_import_receipt, pour cette opération sans le scope.

Outils de plugins

Un administrateur doit activer la surface MCP de chaque plugin. Les outils activés apparaissent dans tools/list sous la forme <pluginId>__<localName> et exigent mcp:tools ou mcp:tools:<pluginId> pour les appels authentifiés par jeton. EmDash vérifie aussi la permission déclarée par la route du plugin et consigne le plugin, l’outil, la route et l’acteur dans le journal d’audit.

Comme les outils de plugins dépendent de l’installation, ils ne font pas partie de l’inventaire statique ci-dessus.

Découverte OAuth

Les clients MCP découvrent le serveur d’autorisation à partir des métadonnées de la ressource protégée :

GET /.well-known/oauth-protected-resource

La réponse désigne /_emdash/api/mcp comme la ressource protégée et renvoie vers le serveur d’autorisation. Les clients lisent ensuite ses métadonnées à l’adresse suivante :

GET /.well-known/oauth-authorization-server/_emdash

Ce document fournit les points de terminaison actuels d’autorisation, de jeton, d’enregistrement et d’autorisation d’appareil, les scopes et types d’octroi pris en charge, ainsi que la méthode PKCE S256. Utilisez les valeurs découvertes plutôt que de coder en dur les routes du protocole OAuth.

Une requête MCP non authentifiée renvoie une réponse 401 avec l’URL de découverte :

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"

Erreurs

Un échec d’outil comporte isError: true. Le premier bloc de texte commence par un code stable, et _meta.code le répète pour les clients qui lisent les métadonnées structurées :

{
	"content": [{ "type": "text", "text": "[NOT_FOUND] Collection 'articles' not found" }],
	"isError": true,
	"_meta": { "code": "NOT_FOUND" }
}

Un refus dû au verrou d’édition d’une entrée indique le détenteur dans _meta.details :

{
	"content": [{ "type": "text", "text": "[ENTRY_LOCKED] Ada is holding this entry" }],
	"isError": true,
	"_meta": {
		"code": "ENTRY_LOCKED",
		"details": {
			"userId": "01JB...",
			"userName": "Ada",
			"acquiredAt": "2026-05-01T09:12:04.117Z",
			"expiresAt": "2026-05-01T09:19:04.117Z"
		}
	}
}

Les échecs d’authentification utilisent des codes tels que INSUFFICIENT_SCOPE et INSUFFICIENT_PERMISSIONS. Les échecs de transport utilisent le code d’erreur interne JSON-RPC -32603 et n’exposent pas l’exception sous-jacente.