Referencia del Servidor MCP

En esta página

EmDash incluye un servidor Model Context Protocol (MCP) integrado en /_emdash/api/mcp que expone operaciones de gestión de contenido como herramientas para asistentes de IA.

Esta página cubre los detalles del protocolo: autenticación, transporte, especificaciones de herramientas, descubrimiento OAuth y manejo de errores.

Autenticación

El servidor MCP admite tres métodos de autenticación:

MétodoCómo funciona
OAuth 2.1 Authorization Code + PKCEFlujo estándar para clientes MCP. El usuario aprueba scopes en el navegador.
Personal Access Token (PAT)Tokens de larga duración ec_pat_* creados en el panel de administración.
Device FlowFlujo estilo CLI donde apruebas un código en el navegador. Usado por emdash login.

Las cookies de sesión (de la interfaz de administración) también funcionan pero no son prácticas para clientes MCP externos.

Scopes

Los tokens tienen scopes para limitar qué operaciones puede realizar un cliente. Los scopes se solicitan durante la autorización OAuth y se aplican en cada llamada de herramienta.

ScopeOtorga acceso a
content:readListar, obtener, comparar y buscar contenido. Listar taxonomías, términos de taxonomía y menús.
content:writeCrear, actualizar, eliminar, publicar, despublicar, programar, desprogramar, duplicar y restaurar contenido. Otorga implícitamente taxonomies:manage y menus:manage para compatibilidad con tokens emitidos antes de que existieran esos scopes.
media:readListar y obtener elementos de medios.
media:writeRegistrar (crear), actualizar y eliminar metadatos de medios.
schema:readListar colecciones y obtener esquemas de colecciones.
schema:writeCrear y eliminar colecciones y campos.
taxonomies:manageCrear, actualizar y eliminar términos de taxonomía.
menus:manageCrear, actualizar y eliminar menús de navegación y sus elementos.
settings:readLeer configuraciones del sitio.
settings:manageActualizar configuraciones del sitio.
adminAcceso completo a todas las operaciones.

El scope admin otorga acceso a todo. La autenticación basada en sesión (sin token) también tiene acceso completo según el rol del usuario.

content:write otorga implícitamente taxonomies:manage y menus:manage para que los tokens de acceso personal emitidos antes de la separación de esos scopes sigan funcionando sin reemisión. Los nuevos tokens deben solicitar los scopes granulares.

Requisitos de Rol

Además de los scopes, algunas herramientas requieren un rol RBAC mínimo. Ambos deben cumplirse — un token con el scope correcto aún falla si el rol del usuario que llama es demasiado bajo.

OperaciónRol mínimo
Lectura de contenidoSubscriber (10) para elementos publicados; Contributor (20) para borradores, programados, papelera y revisiones
Creación de contenidoContributor (20)
Editar propio / eliminar propioAuthor (30)
Publicar contenidoAuthor (30) para elementos propios; Editor (40) para actuar sobre elementos de otros
Lectura de esquemaEditor (40)
Escritura de esquemaAdmin (50)
Gestión de taxonomíasEditor (40)
Gestión de menúsEditor (40)
Lectura de configuracionesEditor (40)
Gestión de configuracionesAdmin (50)
Subir medios (media_create)Author (30)

Consulta la guía de Autenticación para definiciones de roles.

Transporte

El servidor utiliza el transporte HTTP de transmisión en modo sin estado. Cada solicitud es independiente — no hay sesiones ni conexiones de larga duración.

  • POST /_emdash/api/mcp — Enviar llamadas de herramientas JSON-RPC
  • GET /_emdash/api/mcp — Devuelve 405 (sin SSE en modo sin estado)
  • DELETE /_emdash/api/mcp — Devuelve 405 (no hay sesión para cerrar)

Las respuestas siguen el formato JSON-RPC 2.0. Los errores usan códigos de error JSON-RPC estándar, con códigos específicos de MCP para fallos de scope y permisos.

Herramientas

El servidor expone 45 herramientas en ocho dominios: contenido, esquema, medios, búsqueda, taxonomías, menús, revisiones y configuraciones. Cada herramienta devuelve resultados como contenido de texto JSON, o un mensaje de error con isError: true en caso de fallo.

Herramientas de Contenido

content_list

Listar elementos de contenido en una colección con filtrado y paginación opcionales.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección (ej. posts, pages)
statusstringNoFiltro: draft, published o scheduled
limitintegerNoMáx. elementos a devolver (1-100, predeterminado 50)
cursorstringNoCursor de paginación de una respuesta anterior
orderBystringNoCampo por el que ordenar (ej. created_at, updated_at)
orderstringNoDirección de ordenamiento: asc o desc (predeterminado desc)
localestringNoFiltrar por locale (ej. en, fr). Solo relevante con i18n.

Scope: content:read | Solo lectura:

content_get

Obtener un solo elemento de contenido por ID o slug. Devuelve todos los valores de campo, metadatos y un token _rev para concurrencia optimista.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido (ULID) o slug
localestringNoLocale para búsqueda de slug. Los IDs son globalmente únicos.

Scope: content:read | Solo lectura:

content_create

Crear un nuevo elemento de contenido. El objeto data debe contener valores de campo que coincidan con el esquema de la colección — usa schema_get_collection para verificar qué campos están disponibles. Los elementos se crean como draft por defecto.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
dataobjectValores de campo como pares clave-valor
slugstringNoSlug de URL (autogenerado del título si se omite)
statusstringNoEstado inicial: draft o published (predeterminado draft)
localestringNoLocale para este contenido (predeterminado al predeterminado del sitio)
translationOfstringNoID del elemento del cual este es una traducción

Scope: content:write

content_update

Actualizar un elemento de contenido existente. Solo incluye campos que desees cambiar — los campos no especificados permanecen sin cambios.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug
dataobjectNoValores de campo a actualizar
slugstringNoNuevo slug de URL
statusstringNoNuevo estado: draft o published
_revstringNoToken de revisión de content_get para detección de conflictos

Scope: content:write

content_delete

Eliminar suavemente un elemento de contenido moviéndolo a la papelera. Usa content_restore para deshacer, o content_permanent_delete para eliminarlo para siempre.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:write | Destructivo:

content_restore

Restaurar un elemento de contenido eliminado suavemente desde la papelera.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:write

content_permanent_delete

Eliminar permanente e irreversiblemente un elemento de contenido en la papelera. El elemento debe estar primero en la papelera.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:write | Destructivo:

content_publish

Publicar un elemento de contenido, haciéndolo visible en el sitio. Crea una revisión publicada del borrador actual. Las ediciones posteriores crean un nuevo borrador sin afectar la versión en vivo hasta que se vuelva a publicar.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:write

content_unpublish

Revertir un elemento publicado a estado de borrador. Ya no será visible en el sitio en vivo pero su contenido se preserva.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:write

content_schedule

Programar un elemento de contenido para publicación futura. Se publicará automáticamente en la fecha/hora especificada.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug
scheduledAtstringFecha/hora ISO 8601 (ej. 2026-06-01T09:00:00Z)

Scope: content:write

content_unschedule

Cancelar una publicación previamente programada. El elemento mantiene su estado actual; solo se borra el timestamp scheduledAt. Idempotente — llamar sobre un elemento no programado es un no-op.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:write

content_compare

Comparar la versión publicada (en vivo) de un elemento de contenido con su borrador actual. Devuelve ambas versiones y una bandera indicando si hay cambios.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:read | Solo lectura:

content_discard_draft

Descartar el borrador actual y revertir a la última versión publicada. Solo funciona en elementos que se han publicado al menos una vez.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:write | Destructivo:

content_list_trashed

Listar elementos de contenido eliminados suavemente en la papelera de una colección.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
limitintegerNoMáx. elementos (1-100, predeterminado 50)
cursorstringNoCursor de paginación

Scope: content:read | Solo lectura:

content_duplicate

Crear una copia de un elemento de contenido existente. El duplicado se crea como borrador con “(Copy)” agregado al título y un slug autogenerado.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug a duplicar

Scope: content:write

content_translations

Obtener todas las variantes de locale de un elemento de contenido. Devuelve el grupo de traducción y un resumen de cada versión de locale. Solo relevante cuando i18n está habilitado.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug

Scope: content:read | Solo lectura:

Herramientas de Esquema

schema_list_collections

Listar todas las colecciones de contenido definidas en el CMS. Devuelve slug, etiqueta, características soportadas y timestamps.

Sin parámetros.

Scope: schema:read | Rol mínimo: Editor | Solo lectura:

schema_get_collection

Obtener información detallada sobre una colección incluyendo todas las definiciones de campo. Los campos describen el modelo de datos: nombre, tipo, restricciones y reglas de validación. Usa esto para entender qué esperan content_create y content_update.

ParámetroTipoRequeridoDescripción
slugstringSlug de colección (ej. posts)

Scope: schema:read | Rol mínimo: Editor | Solo lectura:

schema_create_collection

Crear una nueva colección de contenido. Esto crea una tabla de base de datos y definición de esquema. El slug debe ser alfanumérico en minúsculas con guiones bajos, comenzando con una letra.

ParámetroTipoRequeridoDescripción
slugstringIdentificador único (/^[a-z][a-z0-9_]*$/)
labelstringNombre para mostrar (plural, ej. “Publicaciones del Blog”)
labelSingularstringNoNombre para mostrar singular
descriptionstringNoDescripción de esta colección
iconstringNoNombre de icono para la interfaz de administración
supportsstring[]NoCaracterísticas: drafts, revisions, preview, scheduling, search (predeterminado: ['drafts', 'revisions'])

Scope: schema:write | Rol mínimo: Admin

schema_delete_collection

Eliminar una colección y su tabla de base de datos. Esto es irreversible y elimina todo el contenido en la colección.

ParámetroTipoRequeridoDescripción
slugstringSlug de colección a eliminar
forcebooleanNoForzar eliminación incluso si la colección tiene contenido

Scope: schema:write | Rol mínimo: Admin | Destructivo:

schema_create_field

Agregar un nuevo campo al esquema de una colección. Esto agrega una columna a la tabla de la base de datos.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
slugstringIdentificador de campo (/^[a-z][a-z0-9_]*$/)
labelstringNombre para mostrar
typestringTipo de dato (ver abajo)
requiredbooleanNoSi el campo es requerido
uniquebooleanNoSi los valores deben ser únicos
defaultValueanyNoValor predeterminado para nuevos elementos
validationobjectNoRestricciones: min, max, minLength, maxLength, pattern, options
optionsobjectNoConfiguración del widget: collection (para referencias), rows (para textarea)
searchablebooleanNoIncluir en índice de búsqueda de texto completo
translatablebooleanNoSi este campo es traducible (predeterminado true)

Tipos de campo: string, text, number, integer, boolean, datetime, select, multiSelect, portableText, image, file, reference, json, slug.

Para tipos select y multiSelect, proporciona valores permitidos en validation.options.

Scope: schema:write | Rol mínimo: Admin

schema_delete_field

Eliminar un campo de una colección. Esto elimina la columna y todos los datos en ese campo. Irreversible.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
fieldSlugstringSlug de campo a eliminar

Scope: schema:write | Rol mínimo: Admin | Destructivo:

Herramientas de Medios

media_list

Listar archivos de medios subidos con filtrado opcional por tipo MIME y paginación.

ParámetroTipoRequeridoDescripción
mimeTypestringNoFiltrar por prefijo de tipo MIME (ej. image/, application/pdf)
limitintegerNoMáx. elementos (1-100, predeterminado 50)
cursorstringNoCursor de paginación

Scope: media:read | Solo lectura:

media_create

Registrar un archivo de medios que ya ha sido subido al almacenamiento. El llamador es responsable de colocar el archivo en storageKey (típicamente usando una URL de carga firmada de la interfaz de administración o una API separada). Esta herramienta persiste el registro de metadatos para que el archivo sea descubrible vía media_list / media_get y pueda ser referenciado por contenido.

ParámetroTipoRequeridoDescripción
filenamestringNombre de archivo original (ej. logo.png)
mimeTypestringTipo MIME (ej. image/png)
storageKeystringRuta/clave de almacenamiento donde se subió el archivo
sizeintegerNoTamaño del archivo en bytes
widthintegerNoAncho de imagen en píxeles
heightintegerNoAlto de imagen en píxeles
contentHashstringNoHash del contenido del archivo (para deduplicación)
blurhashstringNoBlurhash para marcadores de posición de imagen
dominantColorstringNoCadena de color hex para el color dominante de la imagen

Scope: media:write | Rol mínimo: Author

media_get

Obtener detalles de un solo archivo de medios por ID. Devuelve metadatos incluyendo nombre de archivo, tipo MIME, tamaño, dimensiones, texto alternativo y URL.

ParámetroTipoRequeridoDescripción
idstringID de elemento de medios

Scope: media:read | Solo lectura:

media_update

Actualizar metadatos de un archivo de medios subido. El archivo en sí no puede ser cambiado.

ParámetroTipoRequeridoDescripción
idstringID de elemento de medios
altstringNoTexto alternativo para accesibilidad
captionstringNoTexto de pie de foto
widthintegerNoAncho de imagen en píxeles
heightintegerNoAlto de imagen en píxeles

Scope: media:write

media_delete

Eliminar permanentemente un archivo de medios. Elimina el registro de base de datos y el archivo del almacenamiento. El contenido que referencia estos medios tendrá referencias rotas.

ParámetroTipoRequeridoDescripción
idstringID de elemento de medios

Scope: media:write | Destructivo:

Herramienta de Búsqueda

Búsqueda de texto completo en colecciones de contenido. Las colecciones deben tener search en su lista supports y los campos deben estar marcados como searchable.

ParámetroTipoRequeridoDescripción
querystringTexto de consulta de búsqueda
collectionsstring[]NoLimitar búsqueda a slugs de colección específicos
localestringNoFiltrar resultados por locale
limitintegerNoMáx. resultados (1-50, predeterminado 20)

Scope: content:read | Solo lectura:

Herramientas de Taxonomía

taxonomy_list

Listar todas las definiciones de taxonomía (ej. categorías, etiquetas). Devuelve nombre, etiqueta, si es jerárquica y colecciones asociadas.

Sin parámetros.

Scope: content:read | Solo lectura:

taxonomy_list_terms

Listar términos en una taxonomía con paginación.

ParámetroTipoRequeridoDescripción
taxonomystringNombre de taxonomía (ej. categories, tags)
limitintegerNoMáx. elementos (1-100, predeterminado 50)
cursorstringNoCursor de paginación

Scope: content:read | Solo lectura:

taxonomy_create_term

Crear un nuevo término en una taxonomía. Para taxonomías jerárquicas, especifica un parentId para crear un término hijo. La cadena de ancestros del padre no debe exceder 100 niveles.

ParámetroTipoRequeridoDescripción
taxonomystringNombre de taxonomía
slugstringIdentificador seguro para URL
labelstringNombre para mostrar
parentIdstringNoID de término padre (para taxonomías jerárquicas)
descriptionstringNoDescripción del término

Scope: taxonomies:manage | Rol mínimo: Editor

taxonomy_update_term

Actualizar un término existente en una taxonomía. Cualquier campo puede omitirse para dejarlo sin cambios. Renombrar un slug no debe colisionar con otro término en la misma taxonomía. Establece parentId en null para desasociar de un padre. El nuevo padre debe existir, pertenecer a la misma taxonomía y no introducir un ciclo.

ParámetroTipoRequeridoDescripción
taxonomystringNombre de taxonomía
termSlugstringSlug actual del término a actualizar
slugstringNoNuevo slug (debe ser único en la taxonomía)
labelstringNoNuevo nombre para mostrar
parentIdstring | nullNoNuevo ID de término padre; null para desasociar
descriptionstringNoNueva descripción

Scope: taxonomies:manage | Rol mínimo: Editor

taxonomy_delete_term

Eliminar permanentemente un término de una taxonomía. Cualquier contenido etiquetado con el término pierde la asociación. No puede eliminar un término que tenga hijos — elimina primero los hijos.

ParámetroTipoRequeridoDescripción
taxonomystringNombre de taxonomía
termSlugstringSlug del término a eliminar

Scope: taxonomies:manage | Rol mínimo: Editor | Destructivo:

Herramientas de Menú

Listar menús de navegación. Los menús son por locale: pasa locale para devolver solo las filas de un locale, u omítelo para listar cada variante de locale.

ParámetroTipoRequeridoDescripción
localestringNoFiltrar por locale (omitir para todas las variantes de locale)

Scope: content:read | Solo lectura:

Obtener un menú por nombre incluyendo todos sus elementos en orden. Los elementos tienen una etiqueta, URL, tipo y padre opcional para anidamiento. Cuando el mismo nombre de menú existe en múltiples locales, pasa locale para resolver la traducción prevista.

ParámetroTipoRequeridoDescripción
namestringNombre de menú (ej. main, footer)
localestringNoLocale para resolver el menú

Scope: content:read | Solo lectura:

Crear un nuevo menú de navegación. El name es el identificador estable usado por las plantillas del sitio; label es el nombre legible para humanos mostrado en el admin. Los menús son por locale, así que pasa locale cuando el mismo nombre de menú existe en múltiples traducciones. Agrega elementos después con menu_set_items. Si translationOf está establecido, locale también debe estar establecido.

ParámetroTipoRequeridoDescripción
namestringIdentificador estable (/^[a-z][a-z0-9_]*$/)
labelstringNombre para mostrar para el admin
localestringNoLocale para este menú (ej. fr-fr)
translationOfstringNoID de menú existente desde el cual crear esta variante de locale

Scope: menus:manage | Rol mínimo: Editor

Actualizar la etiqueta de un menú. El name (identificador estable) no puede cambiarse. En instalaciones multi-locale, pasa locale para que se actualice la traducción correcta.

ParámetroTipoRequeridoDescripción
namestringNombre de menú a actualizar
labelstringNueva etiqueta para mostrar
localestringNoLocale del menú a actualizar

Scope: menus:manage | Rol mínimo: Editor

Eliminar un menú y todos sus elementos. No puede deshacerse. En instalaciones multi-locale, pasa locale para que solo se elimine la traducción prevista.

ParámetroTipoRequeridoDescripción
namestringNombre de menú a eliminar
localestringNoLocale del menú a eliminar

Scope: menus:manage | Rol mínimo: Editor | Destructivo:

Reemplazar la lista completa de elementos de un menú en una sola llamada. Atómico: los elementos existentes se eliminan y la nueva lista se inserta en el orden proporcionado. Usa esto en lugar de operaciones de agregar/eliminar por elemento para que el orden resultante y los enlaces padre sean inequívocos. En instalaciones multi-locale, pasa locale para que solo se reescriba la traducción prevista.

Los elementos se posicionan por índice de array. El anidamiento se expresa mediante parentIndex — un elemento con parentIndex: 0 está anidado bajo el elemento en el índice 0. El padre debe aparecer antes en la lista. Los elementos sin parentIndex son de nivel superior.

ParámetroTipoRequeridoDescripción
namestringNombre de menú a actualizar
localestringNoLocale del menú a reescribir
itemsMenuItem[]Lista ordenada de elementos de menú (ver abajo)

Cada MenuItem tiene:

CampoTipoRequeridoDescripción
labelstringTexto de visualización del elemento
typestringUno de custom, page, post, taxonomy, collection
customUrlstringNoURL para elementos type: "custom" (ignorado de otro modo)
referenceCollectionstringNoSlug de colección objetivo para referencias de contenido
referenceIdstringNoID de contenido / término objetivo para referencias
titleAttrstringNoAtributo HTML title
targetstringNoAtributo HTML target (ej. _blank)
cssClassesstringNoClases CSS separadas por espacios
parentIndexintegerNoÍndice de array del elemento padre. Omitir para elementos de nivel superior.

Scope: menus:manage | Rol mínimo: Editor

Herramientas de Revisión

revision_list

Listar historial de revisión para un elemento de contenido, más reciente primero. Requiere que la colección soporte revisions.

ParámetroTipoRequeridoDescripción
collectionstringSlug de colección
idstringID de elemento de contenido o slug
limitintegerNoMáx. revisiones (1-50, predeterminado 20)

Scope: content:read | Solo lectura:

revision_restore

Restaurar un elemento de contenido a una revisión anterior. Reemplaza el borrador actual con los datos de la revisión especificada. No se publica automáticamente — usa content_publish después si es necesario.

ParámetroTipoRequeridoDescripción
revisionIdstringID de revisión a restaurar

Scope: content:write

Herramientas de Configuración

Configuraciones del sitio — título, tagline, logo, favicon, URL canónica, tamaño de página predeterminado, formato de fecha y hora, handles sociales y valores predeterminados SEO.

settings_get

Obtener todas las configuraciones del sitio. Las referencias de medios (logo, favicon, seo.defaultOgImage) incluyen URLs resueltas junto con la mediaId subyacente. Los valores no establecidos se omiten de la respuesta.

Sin parámetros.

Scope: settings:read | Rol mínimo: Editor | Solo lectura:

settings_update

Actualizar una o más configuraciones del sitio. Actualización parcial: solo los campos proporcionados se cambian; los campos omitidos se dejan como están. Devuelve el objeto de configuraciones completo después de la actualización.

Para establecer una referencia de medios (logo, favicon, seo.defaultOgImage), pasa un objeto con mediaId (y alt opcional). El elemento de medios debe existir ya — usa media_create primero.

ParámetroTipoRequeridoDescripción
titlestringNoTítulo del sitio
taglinestringNoDescripción breve mostrada junto al título
logoMediaRefNoReferencia de medios de logo ({ mediaId, alt? })
faviconMediaRefNoReferencia de medios de favicon
urlstringNoURL canónica del sitio (http o https). Cadena vacía la borra.
postsPerPageintegerNoTamaño de página predeterminado para listados de contenido (1-100)
dateFormatstringNoCadena de token de formato de fecha
timezonestringNoIdentificador de zona horaria IANA
socialobjectNoHandles sociales — twitter, github, facebook, instagram, linkedin, youtube
seoobjectNoValores predeterminados SEO (ver abajo)

El objeto seo acepta:

CampoTipoDescripción
titleSeparatorstringSeparador entre título de página y título del sitio (ej. " | " para barra vertical)
defaultOgImageMediaRefImagen Open Graph predeterminada cuando el contenido no tiene ninguna
robotsTxtstringCuerpo personalizado de robots.txt. Omitir para usar el predeterminado de EmDash.
googleVerificationstringToken de verificación de Google Search Console
bingVerificationstringToken de verificación de Bing Webmaster Tools

Scope: settings:manage | Rol mínimo: Admin

Descubrimiento OAuth

La mayoría de los clientes MCP manejan esto por ti; esta sección es para construir un cliente MCP contra EmDash directamente. Los clientes que soportan OAuth 2.1 descubren cómo autenticarse a partir de dos documentos de metadatos que el servidor publica:

Metadatos de Recurso Protegido

Solicita los metadatos de recurso protegido en el siguiente endpoint:

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

El servidor responde con el identificador de recurso, su servidor de autorización y scopes soportados:

{
  "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",
    "taxonomies:manage", "menus:manage",
    "settings:read", "settings:manage",
    "admin"
  ],
  "bearer_methods_supported": ["header"]
}

Metadatos del Servidor de Autorización

Solicita los metadatos del servidor de autorización en el siguiente endpoint:

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

El servidor responde con los endpoints, scopes y tipos de grant que soporta:

{
  "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"
}

Cuando una solicitud no autenticada llega al endpoint MCP, el servidor devuelve:

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

Esto dispara el flujo de descubrimiento estándar del cliente MCP.

Manejo de Errores

Los errores de herramientas se devuelven como contenido de texto con isError: true. El mensaje tiene un prefijo con un [CODE] estable, y el mismo código se repite en _meta.code:

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

Los errores de scope y permisos usan el mismo sobre de error de herramienta:

{
  "content": [
    { "type": "text", "text": "[INSUFFICIENT_SCOPE] Insufficient scope: requires content:write" }
  ],
  "isError": true,
  "_meta": { "code": "INSUFFICIENT_SCOPE" }
}

Los errores a nivel de transporte (configuración errónea del servidor, excepciones no manejadas) devuelven el código de error JSON-RPC -32603 (Internal error) sin revelar detalles de implementación.