O EmDash expõe um servidor Model Context Protocol (MCP) integrado em /_emdash/api/mcp. Os clientes MCP o usam para ler e gerenciar conteúdo, bylines, esquemas, mídia, taxonomias, menus, revisões e configurações, e para exportar ou importar o site inteiro.
Autenticação
O endpoint MCP exige um token Bearer. O EmDash oferece suporte a estes fluxos de token:
| Método | Uso |
|---|---|
| OAuth 2.1 Authorization Code with Proof Key for Code Exchange (PKCE) | Clientes MCP interativos. O usuário aprova no navegador os escopos solicitados. |
| Personal access token | Acesso de longa duração para um cliente ou uma automação. Os tokens usam o prefixo ec_pat_ e são criados no admin. |
| OAuth 2.0 Device Authorization Grant | Clientes de linha de comando que pedem ao usuário que aprove um código no navegador. O emdash login usa esse fluxo. |
Cookies de sessão não autenticam o endpoint MCP.
Escopos
Os tokens OAuth e os tokens de acesso pessoal limitam quais ferramentas um cliente pode chamar. O papel do usuário é verificado separadamente, então um escopo nunca concede uma permissão que o usuário não tem.
| Escopo | Acesso |
|---|---|
content:read | Ler e pesquisar conteúdo, bylines, taxonomias, termos, menus e revisões. Conteúdo semelhante a rascunho também exige a permissão content:read_drafts do usuário. |
content:write | Criar e alterar conteúdo, bylines e revisões. Também concede taxonomies:manage e menus:manage por compatibilidade com os tokens existentes. |
media:read | Ler registros de mídia. |
media:write | Enviar, registrar, atualizar e excluir mídia. |
schema:read | Ler coleções e campos. |
schema:write | Criar, atualizar e excluir coleções e campos. |
taxonomies:manage | Criar, atualizar e excluir definições de taxonomia e termos. |
menus:manage | Criar, atualizar e excluir menus e itens de menu. |
settings:read | Ler as configurações do site. |
settings:manage | Atualizar as configurações do site. |
mcp:tools | Chamar as ferramentas MCP expostas por qualquer plugin habilitado. |
mcp:tools:<pluginId> | Chamar as ferramentas MCP expostas por um único plugin habilitado. |
transfer:export | Exportar o site inteiro como um pacote do site e baixá-lo. |
transfer:analyze | Enviar um pacote do site e analisá-lo para importação. |
transfer:execute | Iniciar, avançar, cancelar e abandonar uma importação do site. |
admin | Chamar todas as ferramentas do núcleo, incluindo as de transferência de site. As ferramentas de plugins continuam exigindo mcp:tools ou o escopo específico do plugin. |
O escopo admin inclui transfer:export, transfer:analyze e transfer:execute. Cada escopo de transferência concede apenas as próprias ações e exige o papel de administrador. Para permitir que um cliente, como um agente, analise um pacote do site sem exportar nem importar, conceda transfer:analyze em vez de admin.
A página de consentimento do código de autorização permite que o usuário remova escopos solicitados. O EmDash também cruza a requisição com os escopos registrados do cliente e com o papel do usuário, e recusa uma concessão vazia.
Requisitos de papel
A tabela a seguir mostra o papel mínimo para cada capacidade geral. As verificações de propriedade podem exigir um papel mais alto quando um usuário age sobre o conteúdo de outro usuário.
| Capacidade | Papel mínimo |
|---|---|
| Ler conteúdo publicado, mídia, taxonomias, termos e menus | Subscriber |
| Ler rascunhos, conteúdo agendado, lixeira, comparações e revisões | Contributor |
| Criar conteúdo ou enviar mídia | Contributor |
| Editar ou publicar o próprio conteúdo e registrar mídia | Author |
| Gerenciar bylines, taxonomias, menus ou o conteúdo de todos os usuários | Editor |
| Ler esquemas ou configurações | Editor |
| Alterar esquemas ou configurações, excluir conteúdo permanentemente ou reparar o uso de mídia | Admin |
| Exportar ou importar o site inteiro | Admin |
Consulte os papéis de usuário para ver as definições completas dos papéis.
Transporte
O servidor usa HTTP Streamable sem estado. Cada requisição é independente; o servidor não mantém uma sessão MCP nem uma conexão de Server-Sent Events.
| Método | Endpoint | Comportamento |
|---|---|---|
POST | /_emdash/api/mcp | Aceita a inicialização do JSON-RPC, a listagem de ferramentas e as chamadas de ferramentas. |
GET | /_emdash/api/mcp | Retorna 405 Method Not Allowed. |
DELETE | /_emdash/api/mcp | Retorna 405 Method Not Allowed. |
As respostas usam JSON-RPC 2.0. Chame tools/list para obter os esquemas de entrada e as anotações MCP atuais antes de montar uma requisição de ferramenta.
Inventário de ferramentas
O inventário a seguir corresponde às ferramentas estáticas retornadas por tools/list. O título registrado está incluído porque os clientes podem exibi-lo no lugar do nome da ferramenta.
Ferramentas de conteúdo
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
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 |
Ferramentas de byline
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
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 |
Ferramentas de esquema
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
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 |
Ferramentas de mídia
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
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 |
Ferramenta de busca
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
search | Search Content | content:read |
Ferramentas de taxonomia
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
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 |
Ferramentas de menu
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
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 |
Ferramentas de revisão
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
revision_list | List Revisions | content:read |
revision_restore | Restore Revision | content:write |
Ferramentas de configurações
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
settings_get | Get Site Settings | settings:read |
settings_update | Update Site Settings | settings:manage |
Ferramentas de transferência de site
transfer:* significa qualquer um entre transfer:export, transfer:analyze ou transfer:execute. O escopo admin satisfaz todos os requisitos desta tabela.
| Ferramenta | Título registrado | Escopo necessário |
|---|---|---|
site_transfer_capabilities | Get Site Transfer Capabilities | transfer:* |
site_export_start | Start Site Export | transfer:export |
site_export_status | Get Site Export Status | transfer:export |
site_import_analyze | Analyze Site Import | transfer:analyze |
site_import_start | Start Site Import | transfer:execute |
site_import_status | Get Site Import Status | transfer:* |
site_import_resume | Resume Site Import | transfer:execute |
site_import_receipt | Get Site Import Receipt | transfer:* |
site_export_start e site_import_start também aceitam um token sem o escopo quando um administrador aprova a requisição. Para a operação que uma requisição aprovada inicia, site_export_status, site_import_status, site_import_resume e site_import_receipt passam então a aceitar o mesmo token sem o escopo.
Usar os esquemas das ferramentas
tools/list retorna a descrição, o esquema JSON de entrada e as anotações de cada ferramenta. Leia esses metadados antes de montar uma chamada, para que o seu cliente use os campos, os valores permitidos e os limites compatíveis com a versão do EmDash instalada.
Por exemplo, um cliente que atualiza um artigo chama primeiro content_get e guarda o _rev retornado. Depois, ele pode enviar esta requisição 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"
}
}
}
O resultado é retornado como texto JSON no primeiro bloco de conteúdo. Uma ferramenta com um esquema de saída também pode retornar o mesmo valor em structuredContent.
Ciclo de vida do conteúdo e bylines
A referência do ciclo de vida do conteúdo define o comportamento de estados, revisões, permissões, conflitos e hooks compartilhado por MCP, REST, pela CLI e pelo painel de administração.
content_get retorna um valor _rev opaco. Passe-o para content_update, content_publish, content_unpublish, content_schedule ou content_discard_draft. Um valor desatualizado retorna um conflito, então leia o item novamente antes de tentar de novo.
content_update é uma atualização parcial: os campos omitidos mantêm os valores atuais. Atualizar um item publicado prepara um rascunho enquanto a versão ao vivo permanece inalterada. Use content_compare para revisar os valores ao vivo e os do rascunho e, em seguida, chame content_publish para publicar o rascunho ou content_discard_draft para removê-lo. content_delete move um item para a lixeira; somente content_permanent_delete remove permanentemente um item que está na lixeira.
Bylines são créditos reutilizáveis de autor ou colaborador. byline_create pode criar um crédito de convidado ou vincular uma byline a um usuário do CMS. Um usuário só pode ter uma byline por locale. Uma tradução criada com translationOf mantém o usuário da byline de origem, a menos que você passe userId; passe null para deixar a tradução sem vínculo. Passe o ID da byline retornado na entrada bylines aceita por content_create e content_update. Excluir uma byline remove esse crédito do conteúdo e a limpa como byline principal.
Enquanto outro usuário mantém o bloqueio de edição de uma entrada porque a abriu no admin, content_update, content_delete, content_publish, content_unpublish, content_schedule, content_unschedule, content_discard_draft e revision_restore falham com ENTRY_LOCKED, e o erro informa quem a mantém. Ler o item novamente não elimina a recusa. Passe overrideLock: true para gravar mesmo assim.
Traduções
As ferramentas de tradução de conteúdo, bylines, termos de taxonomia e menus retornam todas as variantes de locale do grupo de tradução em questão. Use a entrada translationOf da ferramenta de criação quando o esquema dela oferecer uma; tools/list é a fonte autoritativa para os campos obrigatórios.
content_translations aceita uma coleção e um ID ou slug de conteúdo. As ferramentas de tradução de bylines, termos de taxonomia e menus aceitam o ID de um registro ou o ID compartilhado do grupo de tradução. Um usuário sem acesso a rascunhos vê apenas as traduções de conteúdo publicado.
Esquemas, mídia, taxonomias e menus
As ferramentas de esquema alteram a estrutura do banco de dados. Use schema_get_collection antes de criar conteúdo ou alterar campos; ela retorna os nomes de campo, os tipos, as restrições e as regras de validação disponíveis. Excluir coleções e campos remove o conteúdo ou os valores de campo armazenados e não pode ser desfeito.
Use media_upload para enviar bytes codificados em base64. Os envios estão sujeitos aos limites de tamanho e de tipo MIME configurados, e bytes idênticos podem retornar um item de mídia existente com deduplicated: true.
media_create confirma um envio pendente criado por meio de POST /_emdash/api/media/upload-url. Envie o arquivo com a URL assinada retornada e, depois, chame media_create a partir da mesma conta de usuário com o storageKey retornado. A ferramenta verifica se o arquivo armazenado existe e corresponde ao tamanho informado quando a URL de envio foi solicitada, antes de disponibilizá-lo na biblioteca de mídia.
As definições de taxonomia descrevem a classificação e as coleções às quais ela se aplica; os termos são os valores individuais atribuídos ao conteúdo. Termos hierárquicos podem usar parentId, mas o pai deve pertencer à mesma taxonomia e não pode criar um ciclo. Criar ou atualizar um termo com parentId em uma taxonomia não hierárquica retorna VALIDATION_ERROR. Um termo com filhos precisa ter esses filhos removidos ou movidos antes de ser excluído.
menu_set_items substitui a lista completa de itens de um menu em uma única operação atômica. A ordem do array passa a ser a ordem do menu. O parentIndex de um item aninhado aponta para um item anterior no mesmo array, então coloque cada pai antes dos seus filhos.
media_usage_repair pode processar uma coleção ou todas as coleções e pode levar muito tempo em um site grande. Os status complete, partial, failed e stale são respostas bem-sucedidas da ferramenta. Inspecione o status e as contagens retornados em vez de depender de isError; falhas de autenticação, de validação e de execução inesperadas definem isError: true.
Transferência de site
As ferramentas site_* exportam um site inteiro como um pacote do site e importam um pacote em um site vazio. Elas iniciam e conduzem as operações e retornam resumos limitados. Nunca transportam bytes do pacote, mídia, conteúdo de registros, endereços de e-mail dos principals nem URLs de download. Baixe uma exportação e envie um pacote para importação com a CLI ou a API REST e, depois, faça referência à operação pelo ID.
Toda ferramenta de transferência exige o papel Admin. O papel é verificado antes do escopo, então um chamador que não é administrador recebe INSUFFICIENT_PERMISSIONS e nenhuma requisição de aprovação é criada.
Exportação
site_export_start aceita comments (padrão true) e retorna a nova operação. site_export_status executa uma etapa de exportação limitada a cada chamada e informa a operação e nextRequestInMs. Chame-o novamente após esse intervalo até que nextRequestInMs seja null. Passe advance: false para ler o status sem executar uma etapa. Quando a exportação termina, o resultado também inclui totals: contagens de registros por tipo, número e bytes de mídia e número e bytes de arquivos do pacote.
Importação
Envie o pacote primeiro. O comando emdash site import <file> --analyze da CLI envia e analisa o pacote e imprime o ID da operação.
site_import_analyze executa uma etapa de análise limitada a cada chamada. Repita até que nextRequestInMs seja null; o resultado então inclui um resumo do plano com packageDigest, planDigest, executable, contagens, tamanhos, principals, decisões, transformações, avisos e bloqueios. Cada transformação é listada com o seu code, o kind do registro quando houver um e um count, sem os IDs ou valores aos quais se aplica. Principals, avisos e bloqueios listam no máximo 50 itens cada, com a contagem completa em total. Os principals são listados sem endereços de e-mail, com os IDs de usuário de destino sugeridos e os atualmente mapeados. Passe decisions para mapear principals para IDs de usuário de destino (ou null) e para escolher o título e o slogan do pacote ou do destino. Cada alteração produz um novo planDigest.
site_import_start recebe o ID da operação e o packageDigest e o planDigest do plano mais recente. O plano não pode ter bloqueios. A ferramenta tem destructiveHint: true: depois de iniciada, a importação grava no site e bloqueia as demais gravações até terminar ou até que um administrador a abandone. Mostre o plano ao usuário e obtenha a confirmação dele antes de chamá-la.
site_import_resume executa uma etapa de importação limitada e informa a operação e nextRequestInMs. Chame-o até que nextRequestInMs seja null; é seguro repeti-lo após uma desconexão. site_import_status informa a operação e o número de arquivos enviados sem avançar a importação. site_import_receipt retorna o recibo completo, incluindo receiptDigest, quando a importação termina.
Enquanto uma importação está em execução, e depois que ela falha ou é cancelada até ser abandonada, toda outra ferramenta capaz de gravar falha com TRANSFER_IMPORT_IN_PROGRESS. Isso inclui as ferramentas de plugins. As ferramentas anotadas com readOnlyHint: true e as oito ferramentas site_* continuam funcionando, e initialize e tools/list nunca são bloqueadas. Durante a ativação do uso de mídia, as ferramentas de gravação falham da mesma forma com MEDIA_USAGE_ACTIVATION_IN_PROGRESS.
Os resumos de operação incluem id, kind, state, stage, progress, packageDigest, planDigest, error ({ code } ou null) e carimbos de data e hora. progress é { done, total } etapas, além de records, os registros que uma exportação gravou até o momento, e bytesDone e bytesTotal, quando conhecidos. As ferramentas MCP não podem cancelar nem abandonar uma importação; use a API REST.
Aprovações
Um token com admin ou com o escopo de transferência necessário nunca pede aprovação. Para um token sem nenhum dos dois, como o de um agente que recebeu apenas transfer:analyze, site_export_start e site_import_start são executados quando um administrador aprova a requisição:
- A primeira chamada sem o escopo cria uma requisição de aprovação pendente e falha com
TRANSFER_APPROVAL_REQUIRED. O texto da mensagem e_meta.detailstrazem oapprovalIde o seuexpiresAt. Chamar de novo com os mesmos argumentos e semapprovalIdretorna a mesma requisição aberta. - Um administrador aprova a requisição em Approval requests, dentro de Settings → Transfer, ou com o endpoint de aprovação exclusivo de sessão da API REST. Tokens de API não podem aprovar requisições.
- O cliente repete a chamada com os mesmos argumentos e o
approvalId. A aprovação é consumida quando essa chamada inicia a operação. Se a operação falhar ao iniciar, o cliente pode tentar de novo com o mesmoapprovalIdaté que ele expire.
Uma requisição fica vinculada ao usuário, ao token, à ação e aos argumentos exatos: as opções de exportação, ou o ID da operação de importação e os dois digests. Uma requisição pendente expira 15 minutos depois de feita, e uma aprovada, 15 minutos depois da aprovação. Uma chamada com argumentos diferentes ou outro token, ou com uma aprovação negada, expirada ou já usada, falha com TRANSFER_APPROVAL_INVALID.
site_import_start verifica os digests, o estado da operação e os bloqueios do plano antes de criar uma requisição, então só se pede a um administrador que aprove uma importação que pode ser executada. Uma aprovação precisa de um ID de token, então um chamador sem ele recebe INSUFFICIENT_SCOPE.
Depois que uma chamada aprovada inicia uma operação, o mesmo usuário e o mesmo token podem chamar site_export_status, ou site_import_status, site_import_resume e site_import_receipt, para essa operação sem o escopo.
Ferramentas de plugins
Um administrador deve habilitar a superfície MCP de cada plugin. As ferramentas habilitadas aparecem em tools/list como <pluginId>__<localName> e exigem mcp:tools ou mcp:tools:<pluginId> nas chamadas autenticadas por token. O EmDash também verifica a permissão declarada pela rota do plugin e registra o plugin, a ferramenta, a rota e o ator no log de auditoria.
Como as ferramentas de plugins dependem da instalação, elas não fazem parte do inventário estático acima.
Descoberta do OAuth
Os clientes MCP descobrem o servidor de autorização a partir dos metadados do recurso protegido:
GET /.well-known/oauth-protected-resource
A resposta identifica /_emdash/api/mcp como o recurso protegido e aponta para o servidor de autorização. Em seguida, os clientes leem os metadados dele em:
GET /.well-known/oauth-authorization-server/_emdash
Esse documento fornece os endpoints atuais de autorização, token, registro e autorização de dispositivo, os escopos e tipos de concessão compatíveis e o método PKCE S256. Use os valores descobertos em vez de fixar no código as rotas do protocolo OAuth.
Uma requisição MCP não autenticada retorna uma resposta 401 com a URL de descoberta:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"
Erros
Uma falha de ferramenta tem isError: true. O primeiro bloco de texto começa com um código estável, e _meta.code o repete para os clientes que leem metadados estruturados:
{
"content": [{ "type": "text", "text": "[NOT_FOUND] Collection 'articles' not found" }],
"isError": true,
"_meta": { "code": "NOT_FOUND" }
}
Uma recusa causada pelo bloqueio de edição de uma entrada traz quem o mantém em _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"
}
}
}
As falhas de autenticação usam códigos como INSUFFICIENT_SCOPE e INSUFFICIENT_PERMISSIONS. As falhas de transporte usam o código de erro interno do JSON-RPC -32603 e não expõem a exceção subjacente.