EmDash expone un servidor Model Context Protocol (MCP) integrado en /_emdash/api/mcp. Los clientes MCP lo usan para leer y gestionar contenido, bylines, esquemas, medios, taxonomías, menús, revisiones y ajustes, y para exportar o importar todo el sitio.
Autenticación
El endpoint MCP requiere un token Bearer. EmDash admite estos flujos de token:
| Método | Uso |
|---|---|
| OAuth 2.1 Authorization Code with Proof Key for Code Exchange (PKCE) | Clientes MCP interactivos. El usuario aprueba los scopes solicitados en el navegador. |
| Personal access token | Acceso de larga duración para un cliente o automatización. Los tokens usan el prefijo ec_pat_ y se crean en el admin. |
| OAuth 2.0 Device Authorization Grant | Clientes de línea de comandos que piden al usuario aprobar un código en el navegador. emdash login usa este flujo. |
Las cookies de sesión no autentican el endpoint MCP.
Scopes
Los tokens OAuth y de acceso personal limitan qué herramientas puede invocar un cliente. El rol del usuario se comprueba por separado, así que un scope nunca concede un permiso que el usuario no tenga.
| Scope | Acceso |
|---|---|
content:read | Leer y buscar contenido, bylines, taxonomías, términos, menús y revisiones. El contenido tipo borrador también requiere el permiso content:read_drafts del usuario. |
content:write | Crear y modificar contenido, bylines y revisiones. También concede taxonomies:manage y menus:manage por compatibilidad con tokens existentes. |
media:read | Leer registros de medios. |
media:write | Subir, registrar, actualizar y eliminar medios. |
schema:read | Leer colecciones y campos. |
schema:write | Crear, actualizar y eliminar colecciones y campos. |
taxonomies:manage | Crear, actualizar y eliminar definiciones de taxonomía y términos. |
menus:manage | Crear, actualizar y eliminar menús y elementos de menú. |
settings:read | Leer ajustes del sitio. |
settings:manage | Actualizar ajustes del sitio. |
mcp:tools | Invocar herramientas MCP expuestas por cualquier plugin habilitado. |
mcp:tools:<pluginId> | Invocar herramientas MCP expuestas por un plugin habilitado. |
transfer:export | Exportar todo el sitio como paquete de sitio y descargarlo. |
transfer:analyze | Subir un paquete de sitio y analizarlo para importación. |
transfer:execute | Iniciar, avanzar, cancelar y abandonar una importación de sitio. |
admin | Invocar todas las herramientas core, incluidas las de transferencia de sitio. Las herramientas de plugins siguen requiriendo mcp:tools o el scope específico del plugin. |
El scope admin incluye transfer:export, transfer:analyze y transfer:execute. Cada scope de transferencia concede solo sus propias acciones y requiere el rol de administrador. Para que un cliente, como un agente, analice un paquete de sitio sin exportar ni importar, conceda transfer:analyze en lugar de admin.
La página de consentimiento del código de autorización permite al usuario quitar scopes solicitados. EmDash también intersecta la solicitud con los scopes registrados del cliente y el rol del usuario, y rechaza una concesión vacía.
Requisitos de rol
La siguiente tabla muestra el rol mínimo para la capacidad amplia. Las comprobaciones de propiedad pueden exigir un rol superior cuando un usuario actúa sobre contenido de otro usuario.
| Capacidad | Rol mínimo |
|---|---|
| Leer contenido publicado, medios, taxonomías, términos y menús | Subscriber |
| Leer borradores, contenido programado, papelera, comparaciones y revisiones | Contributor |
| Crear contenido o subir medios | Contributor |
| Editar o publicar contenido propio y registrar medios | Author |
| Gestionar bylines, taxonomías, menús o contenido de todos los usuarios | Editor |
| Leer esquemas o ajustes | Editor |
| Cambiar esquemas o ajustes, eliminar contenido permanentemente o reparar uso de medios | Admin |
| Exportar o importar todo el sitio | Admin |
Consulte roles de usuario para las definiciones completas de roles.
Transporte
El servidor usa HTTP Streamable sin estado. Cada solicitud es independiente; el servidor no mantiene una sesión MCP ni una conexión Server-Sent Events.
| Método | Endpoint | Comportamiento |
|---|---|---|
POST | /_emdash/api/mcp | Acepta inicialización JSON-RPC, listado de herramientas e invocaciones. |
GET | /_emdash/api/mcp | Devuelve 405 Method Not Allowed. |
DELETE | /_emdash/api/mcp | Devuelve 405 Method Not Allowed. |
Las respuestas usan JSON-RPC 2.0. Invoque tools/list para obtener los esquemas de entrada y anotaciones MCP actuales antes de construir una solicitud de herramienta.
Inventario de herramientas
El siguiente inventario coincide con las herramientas estáticas devueltas por tools/list. Se incluye el título registrado porque los clientes pueden mostrarlo en lugar del nombre de la herramienta.
Herramientas de contenido
| 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 |
Herramientas de 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 |
Herramientas de esquema
| 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 |
Herramientas de medios
| 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 |
Herramienta de búsqueda
| Tool | Registered title | Required scope |
|---|---|---|
search | Search Content | content:read |
Herramientas de taxonomía
| 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 |
Herramientas de menú
| 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 |
Herramientas de revisión
| Tool | Registered title | Required scope |
|---|---|---|
revision_list | List Revisions | content:read |
revision_restore | Restore Revision | content:write |
Herramientas de ajustes
| Tool | Registered title | Required scope |
|---|---|---|
settings_get | Get Site Settings | settings:read |
settings_update | Update Site Settings | settings:manage |
Herramientas de transferencia de sitio
transfer:* significa cualquiera de transfer:export, transfer:analyze o transfer:execute. El scope admin satisface todos los requisitos de esta tabla.
| 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 y site_import_start también aceptan un token sin el scope cuando un administrador aprueba la solicitud. Para la operación que inicia una solicitud aprobada, site_export_status, site_import_status, site_import_resume y site_import_receipt aceptan el mismo token sin el scope.
Usar los esquemas de herramientas
tools/list devuelve la descripción, el esquema de entrada JSON y las anotaciones de cada herramienta. Lea esos metadatos antes de construir una invocación para que su cliente use los campos, valores permitidos y límites soportados por la versión de EmDash instalada.
Por ejemplo, un cliente que actualiza un artículo invoca primero content_get y conserva el _rev devuelto. Luego puede enviar esta solicitud 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"
}
}
}
El resultado se devuelve como texto JSON en el primer bloque de contenido. Una herramienta con esquema de salida también puede devolver el mismo valor en structuredContent.
Ciclo de vida del contenido y bylines
La referencia del ciclo de vida del contenido define el estado, la revisión, permisos, conflictos y comportamiento de hooks compartidos por MCP, REST, la CLI y el panel de admin.
content_get devuelve un valor _rev opaco. Páselo a content_update, content_publish, content_unpublish, content_schedule o content_discard_draft. Un valor obsoleto devuelve un conflicto; vuelva a leer el elemento antes de reintentar.
content_update es una actualización parcial: los campos omitidos conservan sus valores actuales. Actualizar un elemento publicado prepara un borrador mientras la versión en vivo permanece sin cambios. Use content_compare para revisar los valores en vivo y en borrador, luego invoque content_publish para publicar el borrador o content_discard_draft para eliminarlo. content_delete mueve un elemento a la papelera; solo content_permanent_delete elimina permanentemente un elemento en la papelera.
Las bylines son créditos reutilizables de autor o colaborador. byline_create puede crear un crédito de invitado o vincular una byline a un usuario del CMS. Pase el ID de byline devuelto en la entrada bylines que aceptan content_create y content_update. Eliminar una byline quita ese crédito del contenido y la borra como byline principal.
Las escrituras MCP no participan en el bloqueo de edición de entrada del admin. La comprobación _rev protege las operaciones que la aceptan, pero otras herramientas de escritura pueden cambiar una entrada mientras un editor la tiene abierta.
Traducciones
Las herramientas de traducción de contenido, byline, término de taxonomía y menú devuelven cada variante de locale en el grupo de traducción correspondiente. Use la entrada translationOf de la herramienta de creación cuando su esquema la proporcione; tools/list es la referencia para los campos requeridos.
content_translations acepta colección e ID o slug de contenido. Las herramientas de traducción de byline, término de taxonomía y menú aceptan el ID de un registro o el ID compartido del grupo de traducción. Un usuario sin acceso a borradores ve solo traducciones de contenido publicado.
Esquemas, medios, taxonomías y menús
Las herramientas de esquema cambian la estructura de la base de datos. Use schema_get_collection antes de crear contenido o cambiar campos; devuelve nombres de campo, tipos, restricciones y reglas de validación disponibles. Eliminar colecciones y campos quita contenido almacenado o valores de campo y no se puede deshacer.
Use media_upload para enviar bytes codificados en base64. Las subidas están sujetas a los límites de tamaño y tipo MIME configurados; bytes idénticos pueden devolver un elemento de medios existente con deduplicated: true.
media_create confirma una subida pendiente creada mediante POST /_emdash/api/media/upload-url. Suba el archivo con la URL firmada devuelta, luego invoque media_create desde la misma cuenta de usuario con el storageKey devuelto. La herramienta comprueba que el archivo almacenado existe y coincide con el tamaño indicado al solicitar la URL de subida antes de hacerlo disponible en la biblioteca de medios.
Las definiciones de taxonomía describen la clasificación y las colecciones a las que aplica; los términos son los valores individuales asignados al contenido. Los términos jerárquicos pueden usar parentId, pero un padre debe pertenecer a la misma taxonomía y no puede crear un ciclo. Crear o actualizar un término con parentId en una taxonomía no jerárquica devuelve VALIDATION_ERROR. Un término con hijos debe tener esos hijos eliminados o movidos antes de poder borrarse.
menu_set_items reemplaza la lista completa de elementos de un menú en una operación atómica. El orden del array se convierte en el orden del menú. El parentIndex de un elemento anidado apunta a un elemento anterior en el mismo array; coloque cada padre antes de sus hijos.
media_usage_repair puede procesar una colección o todas las colecciones y puede ejecutarse mucho tiempo en un sitio grande. Sus estados complete, partial, failed y stale son respuestas exitosas de la herramienta. Inspeccione el estado y los conteos devueltos en lugar de confiar en isError; fallos de autenticación, validación y ejecución inesperada establecen isError: true.
Transferencia de sitio
Las herramientas site_* exportan todo un sitio como paquete de sitio e importan un paquete en un sitio vacío. Inician y conducen operaciones y devuelven resúmenes acotados. Nunca transportan bytes del paquete, medios, contenidos de registros, direcciones de correo de principals ni URLs de descarga. Descargue una exportación y suba un paquete para importar con la CLI o la API REST, luego referencie la operación por su ID.
Toda herramienta de transferencia requiere el rol Admin. El rol se comprueba antes que el scope; un llamador no admin recibe INSUFFICIENT_PERMISSIONS y no se crea solicitud de aprobación.
Exportación
site_export_start acepta comments (predeterminado true) y devuelve la nueva operación. site_export_status ejecuta un paso de exportación acotado en cada llamada e informa la operación y nextRequestInMs. Vuelva a llamar tras ese retraso hasta que nextRequestInMs sea null. Pase advance: false para leer el estado sin ejecutar un paso. Cuando la exportación termina, el resultado también incluye totals: conteos de registros por tipo, conteo y bytes de medios, y conteo y bytes de archivos del paquete.
Importación
Suba el paquete primero. emdash site import <file> --analyze de la CLI lo sube, lo analiza e imprime el ID de operación.
site_import_analyze ejecuta un paso de análisis acotado por llamada. Repítalo hasta que nextRequestInMs sea null; el resultado incluye entonces un resumen del plan con packageDigest, planDigest, executable, conteos, tamaños, principals, decisiones, transformaciones, advertencias y bloqueadores. Cada transformación se lista como su code, el kind del registro cuando lo tiene, y un count, sin los IDs o valores a los que aplica. Principals, advertencias y bloqueadores listan como máximo 50 elementos cada uno, con el conteo total en total. Los principals se listan sin direcciones de correo, con los IDs de usuario destino sugeridos y mapeados actualmente. Pase decisions para mapear principals a IDs de usuario destino (o null) y elegir el título y eslogan del paquete o destino. Cada cambio produce un nuevo planDigest.
site_import_start toma el ID de operación y los packageDigest y planDigest del plan más reciente. El plan no debe tener bloqueadores. La herramienta tiene destructiveHint: true: una vez iniciada, la importación escribe en el sitio y bloquea otras escrituras hasta que termine o un administrador la abandone. Muestre el plan al usuario y obtenga su confirmación antes de invocarla.
site_import_resume ejecuta un paso de importación acotado e informa la operación y nextRequestInMs. Invóquela hasta que nextRequestInMs sea null; es seguro repetir tras una desconexión. site_import_status informa la operación y conteos de archivos subidos sin avanzar la importación. site_import_receipt devuelve el recibo completo, incluido receiptDigest, cuando la importación termina.
Mientras una importación se ejecuta, y tras fallar o cancelarse hasta abandonarla, toda otra herramienta que pueda escribir falla con TRANSFER_IMPORT_IN_PROGRESS. Esto incluye herramientas de plugins. Las herramientas anotadas readOnlyHint: true y las ocho herramientas site_* siguen funcionando, y initialize y tools/list nunca se bloquean. Durante la activación de uso de medios, las herramientas de escritura fallan con MEDIA_USAGE_ACTIVATION_IN_PROGRESS de la misma forma.
Los resúmenes de operación incluyen id, kind, state, stage, progress, packageDigest, planDigest, error ({ code } o null) y marcas de tiempo. progress es { done, total } pasos, más records, los registros que una exportación ha escrito hasta ahora, y bytesDone y bytesTotal cuando se conocen. Las herramientas MCP no pueden cancelar ni abandonar una importación; use la API REST.
Aprobaciones
Un token con admin o el scope de transferencia necesario nunca pide aprobación. Para un token sin ninguno, como un agente con solo transfer:analyze, site_export_start y site_import_start se ejecutan cuando un administrador aprueba la solicitud:
- La primera llamada sin el scope crea una solicitud de aprobación pendiente y falla con
TRANSFER_APPROVAL_REQUIRED. El texto del mensaje y_meta.detailsllevanapprovalIdy suexpiresAt. Volver a llamar con los mismos argumentos y sinapprovalIddevuelve la misma solicitud abierta. - Un administrador aprueba la solicitud en Solicitudes de aprobación en Ajustes → Transfer, o con el endpoint de aprobación solo de sesión de la API REST. Los tokens de API no pueden aprobar solicitudes.
- El cliente repite la llamada con los mismos argumentos y el
approvalId. La aprobación se consume cuando esa llamada inicia la operación. Si la operación no arranca, el cliente puede reintentar con el mismoapprovalIdhasta que expire.
Una solicitud está ligada al usuario, token, acción y argumentos exactos: opciones de exportación, o ID de operación de importación y ambos digests. Una solicitud pendiente expira 15 minutos después de crearse, y una aprobada 15 minutos tras la aprobación. Una llamada con argumentos distintos u otro token, o con aprobación denegada, expirada o usada, falla con TRANSFER_APPROVAL_INVALID.
site_import_start comprueba los digests, el estado de la operación y los bloqueadores del plan antes de crear una solicitud, así un administrador solo se pide aprobar una importación que puede ejecutarse. Una aprobación necesita un ID de token; un llamador sin uno recibe INSUFFICIENT_SCOPE.
Tras una llamada aprobada que inicia una operación, el mismo usuario y token pueden invocar site_export_status, o site_import_status, site_import_resume y site_import_receipt, para esa operación sin el scope.
Herramientas de plugins
Un administrador debe habilitar la superficie MCP de cada plugin. Las herramientas habilitadas aparecen en tools/list como <pluginId>__<localName> y requieren mcp:tools o mcp:tools:<pluginId> para llamadas autenticadas por token. EmDash también comprueba el permiso declarado por la ruta del plugin y registra plugin, herramienta, ruta y actor en el log de auditoría.
Como las herramientas de plugins dependen de la instalación, no forman parte del inventario estático anterior.
Descubrimiento OAuth
Los clientes MCP descubren el servidor de autorización desde los metadatos del recurso protegido:
GET /.well-known/oauth-protected-resource
La respuesta identifica /_emdash/api/mcp como recurso protegido y enlaza al servidor de autorización. Los clientes leen entonces sus metadatos en:
GET /.well-known/oauth-authorization-server/_emdash
Ese documento proporciona los endpoints actuales de autorización, token, registro y autorización de dispositivo, scopes soportados, tipos de grant y el método PKCE S256. Use los valores descubiertos en lugar de codificar fijas las rutas del protocolo OAuth.
Una solicitud MCP no autenticada devuelve 401 con la URL de descubrimiento:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"
Errores
Un fallo de herramienta tiene isError: true. El primer bloque de texto comienza con un código estable, y _meta.code lo repite para clientes que leen metadatos estructurados:
{
"content": [{ "type": "text", "text": "[NOT_FOUND] Collection 'articles' not found" }],
"isError": true,
"_meta": { "code": "NOT_FOUND" }
}
Los fallos de autenticación usan códigos como INSUFFICIENT_SCOPE e INSUFFICIENT_PERMISSIONS. Los fallos de transporte usan el código de error interno JSON-RPC -32603 y no exponen la excepción subyacente.