eMCP for Evolution CMS

Concede a agentes de IA acesso de leitura e escrita à árvore de documentos, variáveis de template e elementos — com permissões de gerenciador, grupos de documentos, bloqueios de elementos e auditoria do manager_log aplicados a cada chamada. Além disso, inclui geradores artisan para criar seus próprios servidores e ferramentas MCP dentro do Evo.

Documentação

Total Downloads Latest Stable Version License

eMCP para Evolution CMS

eMCP é a camada de integração do Evolution CMS para laravel/mcp.

Ele adapta o Laravel MCP ao runtime do Evo com:

  • Publicação de configuração nativa do Evo
  • ACL do manager e controles de escopo da sApi
  • despacho assíncrono opcional via sTask
  • sem necessidade de esqueleto de aplicação Laravel
  • ferramentas MCP de domínio do Evo para árvore de documentos (SiteContent + TVs)

A implementação começa com um portão MVP rigoroso:

  • transporte web
  • modo manager
  • initialize + tools/list

Estilo de design:

  • contrato primeiro (TOOLSET.md + validadores)
  • registro de servidor declarativo e orientado a configuração (config/mcp.php)
  • pipeline de handlers explícito (validate -> authorize -> query -> map -> paginate)

Se você precisar da arquitetura completa e dos contratos, veja DOCS.md (EN) ou DOCS.uk.md (UA). Contrato público canônico de ferramentas: TOOLSET.md. Política de versionamento e BC: PRD.md (seção API Stability Policy). Runbook de operações: OPERATIONS.md.

Requisitos

  • Evolution CMS 3.5.2+
  • PHP 8.3+
  • Composer 2.2+
  • seiger/sapi 1.x (instalado como dependência; usado apenas quando auth.mode = sapi_jwt)
  • seiger/stask 1.x (instalado como dependência)

Instalação

A partir do diretório core do seu Evo:

cd core
php artisan package:installrequire evolution-cms/emcp "*"
php artisan migrate

Publicar Config e Stubs

php artisan vendor:publish --provider="EvolutionCMS\\eMCP\\eMCPServiceProvider" --tag=emcp-config
php artisan vendor:publish --provider="EvolutionCMS\\eMCP\\eMCPServiceProvider" --tag=emcp-mcp-config
php artisan vendor:publish --provider="EvolutionCMS\\eMCP\\eMCPServiceProvider" --tag=emcp-stubs

Arquivos publicados:

  • core/custom/config/cms/settings/eMCP.php
  • core/custom/config/mcp.php
  • core/stubs/mcp-*.stub

Início Rápido (Interno + Externo)

O contrato padrão é agnóstico de conceito e segue o comportamento do Laravel MCP primeiro.

  1. Crie suas classes de servidor/ferramenta MCP:
php artisan make:mcp-server ContentServer
php artisan make:mcp-tool HealthTool

As classes geradas são colocadas em core/custom/app/Mcp/....

  1. Registre o servidor em core/custom/config/mcp.php (servers[]).
  2. Teste a rota interna/do manager:
  • POST /{manager_prefix}/{handle} com sessão do manager e permissão emcp.
  1. Habilite o modo de API externa (se sApi estiver instalado):
  • mantenha mode.api=true em core/custom/config/cms/settings/eMCP.php
  • chame POST /{SAPI_BASE_PATH}/{SAPI_VERSION}/mcp/{handle} com Bearer JWT e os escopos mcp:* necessários.
  • obtenha o JWT de POST /{SAPI_BASE_PATH}/{SAPI_VERSION}/token (endpoint de token da sApi).
  1. Assíncrono opcional:
  • defina queue.driver=stask, garanta que sTask esteja instalado, use o endpoint de despacho para trabalhos de longa duração.

Tokens de acesso pessoal (auth de API padrão)

Desde esta versão, o endpoint da API não precisa de pacotes extras: um usuário do manager cria um token de acesso pessoal e cada requisição roda como esse usuário, com o papel, permissões e grupos de documentos desse usuário — exatamente como se estivessem logados no manager.

  1. Dê ao papel a permissão emcp (administradores a têm após migrate).
  2. Abra Ferramentas → Tokens MCP no manager ({manager_url}/emcp/tokens), ou execute php artisan emcp:token:create <username> --scopes=mcp:read,mcp:call --expires=90.
  3. Conecte o agente:
claude mcp add --transport http evo https://example.com/mcp/content   --header "Authorization: Bearer emcp_..."
# Codex ~/.codex/config.toml
[mcp_servers.evo]
url = "https://example.com/mcp/content"
bearer_token_env_var = "EVO_MCP_TOKEN"

Escopos restringem um token, nunca ampliam o usuário: mcp:read (listar/ler), mcp:call (ferramentas somente leitura), mcp:write (ferramentas evo.write.*, também controladas por security.enable_write_tools), mcp:admin. Tokens são armazenados com hash, podem expirar e são revogados na mesma página ou com emcp:token:revoke. emcp:token:list mostra o que existe.

auth.mode em core/custom/config/cms/settings/eMCP.php seleciona pat (padrão), sapi_jwt (JWT do seiger/sapi, agora também personificando o usuário do JWT) ou none.

Ferramentas de escrita

evo.write.content.update|create|publish, evo.write.elements.save, evo.write.cache.clear mais as ferramentas de leitura evo.elements.list|get. Cada uma revalida a permissão do manager da ação correspondente do manager (save_document, publish_document, save_chunk, new_snippet, ...), acesso a grupos de documentos, locks de elementos, dispara os mesmos eventos OnBefore*FormSave/On*FormSave e grava uma linha em manager_log, para que um administrador veja mudanças da API ao lado das do navegador.

Ferramentas de outros extras

Um extra contribui com ferramentas implementando EvolutionCMS\eMCP\Contracts\ToolProvider e registrando-o no boot() do seu service provider:

if (class_exists(\EvolutionCMS\eMCP\Services\ToolRegistry::class)) {
    app(\EvolutionCMS\eMCP\Services\ToolRegistry::class)->register(new MyToolProvider());
}

Ferramentas são classes Laravel\Mcp\Server\Tool simples; marque as que alteram o site com EvolutionCMS\eMCP\Contracts\WritesSite para que security.enable_write_tools e o escopo mcp:write se apliquem a elas. O código da ferramenta roda como o manager personificado, então evo()->hasPermission() e as próprias proteções do extra se comportam como na página. Alternativamente, liste classes sob mcp.servers[].extra_tools. Exemplo: elcreator/aimage entrega ferramentas aimage.* dessa forma.

Experimente no Docker

cd docker && docker compose up --build      # prints the site URL and a ready-made token
EVO_EXTRAS=elcreator/aimage AIMAGE_API_KEY=... docker compose up --build   # with extras
docker/smoke.sh                             # runs an end-to-end check against it

Filosofia de Design (Leitura Opcional)

Por que Este Produto Existe (4 Perguntas Centrais, Aristóteles)

Esta é a forma mais curta de entender o eMCP como produto, não apenas como pacote.

  1. Causa material: do que ele consiste (limites rígidos):
  • protocolo/runtime de laravel/mcp
  • camada adaptadora do Evo (ServiceProvider, registro, rotas, middleware, publish)
  • integrações opcionais de acesso/assíncrono (sApi, sTask)
  • contratos canônicos (SPEC.md, TOOLSET.md)
  1. Causa formal: que forma o torna um produto (não componentes):
  • um contrato de execução da requisição à resposta auditada
  • um modelo de política para acesso manager/API (ACL + scopes + limits)
  • um contrato público versionado de ferramentas para consumidores do ecossistema
  • um modelo de extensão estável para pacotes de terceiros
  1. Causa eficiente: o que o coloca em movimento (fluxos de trabalho + gatilhos):
  • gatilho interno: chamada MCP do manager (/{manager_prefix}/{handle})
  • gatilho externo: chamada MCP da API (/{SAPI_BASE_PATH}/{SAPI_VERSION}/mcp/{handle})
  • gatilho assíncrono: despacho para o worker sTask para operações longas
  • gatilho de ciclo de vida: instalação/publicação/registro/teste do pacote
  1. Causa final: por que é construído assim:
  • manter a semântica do Laravel MCP intacta
  • manter a integração com o Evo explícita e operável
  • suportar uso MCP interno e externo
  • permitir múltiplas estratégias de orquestração sobre uma fundação MCP neutra

Modelo Conceitual (Lente de Design)

Esta lente ajuda a explicar decisões de arquitetura:

  • teoria dos conjuntos: dados do CMS são conjuntos estruturados (site -> nós -> atributos)
  • sequência de Peano: fluxos de trabalho são transições de estado ordenadas
  • limites de Godel: sistemas de regras autorreferentes precisam de limites rígidos

Implicação prática:

  • o eMCP permanece como camada de contrato/runtime
  • a lógica de orquestração permanece nos pacotes consumidores
  • portões de política/auditoria/humanos evitam que loops recursivos de regras se tornem inseguros

Verificação de Instalação (1 minuto)

Para o Portão A, use o endpoint do manager /{manager_prefix}/{server_handle} (padrão: /emcp/content). O Portão A é protegido pela ACL do manager, então execute as verificações como um manager logado com permissão emcp (cookie de sessão necessário).

  1. Verifique se GET retorna 405 no endpoint MCP:
curl -i -X GET http://localhost/<MANAGER_PREFIX>/<SERVER_HANDLE> \
  -H 'Cookie: evo_session=<MANAGER_SESSION_COOKIE>'
  1. Verifique o JSON-RPC initialize:
curl -i -X POST http://localhost/<MANAGER_PREFIX>/<SERVER_HANDLE> \
  -H 'Cookie: evo_session=<MANAGER_SESSION_COOKIE>' \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":"init-1","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"smoke","version":"1.0.0"}}}'

Esperado:

  • HTTP 200 para initialize válido.
  • MCP-Session-Id presente nos cabeçalhos da resposta.
  • HTTP 405 estável em GET.

Registrar Servidores MCP (estilo Evo)

Diferente do padrão Laravel routes/ai.php, o eMCP registra servidores a partir da configuração.

Exemplo em core/custom/config/mcp.php:

return [
    'redirect_domains' => ['*'],

    'servers' => [
        [
            'handle' => 'content',
            'transport' => 'web',
            'route' => '/mcp/content',
            'class' => EvolutionCMS\eMCP\Servers\ContentServer::class,
            'enabled' => true,
            'auth' => 'sapi_jwt',
            'scopes' => ['mcp:read', 'mcp:call'],
        ],
        [
            'handle' => 'content-local',
            'transport' => 'local',
            'class' => EvolutionCMS\eMCP\Servers\ContentServer::class,
            'enabled' => false,
        ],
    ],
];

Notas:

  • O endpoint do Portão A do manager ainda é /{manager_prefix}/{handle} (por exemplo, /emcp/content).
  • servers[*].route é usado pelo registro de transporte web e se torna relevante externamente no modo API (Portão B+).
  • content-local está desabilitado por padrão para evitar conflito de registro de nomes de ferramentas duplicados com content.

Modelo de Acesso

  • Acesso manager/interno: permissão do Evo emcp
  • Acesso API (via sApi): escopos JWT (mcp:read, mcp:call, mcp:admin)
  • Leituras de domínio (evo.content.*, evo.model.*) são somente leitura por padrão

Interoperabilidade do Ecossistema

O eMCP é a camada de plataforma MCP para o ecossistema Evo:

  • LaravelMcp: contrato de protocolo/runtime upstream (mantido intacto).
  • sApi: kernel de API externa + descoberta de provedores de rota JWT.
  • sTask: execução/progresso assíncrono de tarefas/worker.
  • eAi: o runtime de IA pode chamar ferramentas MCP via modo manager ou API.
  • dAi: a UI de orquestração do lado do manager pode consumir ferramentas eMCP como um contrato estável.

Isso mantém o núcleo declarativo e neutro: uma fundação MCP para múltiplos conceitos de orquestração.

Ferramentas de Domínio do Evo

  • Implementadas agora: evo.content.search|get|root_tree|descendants|ancestors|children|siblings
  • Opcionais (implementadas): evo.content.neighbors|prev_siblings|next_siblings|children_range|siblings_range
  • Consultas cientes de TV via with_tvs, tv_filters, tv_order estruturados
  • evo.model.list|get implementado com projeção explícita de allowlist por modelo e blacklist de defesa em profundidade para campos sensíveis

Comandos Artisan

Do Laravel MCP (disponíveis via adaptador eMCP):

php artisan make:mcp-server ContentServer
php artisan make:mcp-tool ListResourcesTool
php artisan make:mcp-resource DocsResource
php artisan make:mcp-prompt SummaryPrompt
php artisan mcp:start content-local

Para mcp:start content-local, primeiro habilite content-local em core/custom/config/mcp.php e desabilite entradas de servidor conflitantes se elas expuserem nomes de ferramentas idênticos.

Servidores de terceiros

Registre ferramentas específicas do projeto em uma classe Laravel\Mcp\Server separada e adicione essa classe como uma entrada própria em core/custom/config/mcp.php. Nomes de ferramentas de terceiros não devem usar o namespace reservado evo.*. Não subclassifique o ContentServer do pacote apenas para anexar ferramentas do projeto, porque o conjunto de ferramentas evo.* herdado é de propriedade do pacote e o registro rejeitará esse servidor externo.

php artisan emcp:test --server=<handle> executa verificações genéricas de initialize e tools/list para servidores de terceiros. Quando a classe selecionada é o ContentServer canônico do eMCP, ele também verifica o conjunto de ferramentas Evolution necessário. Use php artisan emcp:list-servers para ver tanto os servidores aceitos quanto as razões concretas pelas quais entradas configuradas foram rejeitadas.

Comandos operacionais do eMCP:

  • php artisan emcp:test
  • php artisan emcp:list-servers
  • php artisan emcp:sync-workers
  • composer run governance:update-lock
  • composer run ci:check
  • composer run benchmark:run
  • composer run benchmark:leaderboard
  • composer run test:integration:clean-install

Verificações do Repositório (para primeira execução no workspace do pacote)

Se você está validando este repositório diretamente:

composer run check
make test
composer run ci:check
make benchmark
make leaderboard

Essas verificações validam composer.json e executam lint de sintaxe PHP nas fontes do pacote.

Demo de um clique + verificação MCP completa:

make demo-all

Este alvo instala o Evo de demonstração, inicia php -S, emite JWT sApi, executa php artisan emcp:test e depois executa composer run test com integração de runtime HTTP habilitada. Após a execução, evidências detalhadas são gravadas em:

  • demo/logs.md (informações de token/auth mascaradas, payloads de requisição MCP, status HTTP, respostas, comandos de verificação manual, além de sondas negativas: 401/403/413/415/409/429 e sanidade evo.model.get(User))
  • demo/logs.md também inclui prova de ciclo de vida local sTask (queued -> completed) via php artisan stask:worker no runtime de demonstração.
  • /tmp/emcp-demo-php-server.log (log do servidor embutido do PHP)

Se a autenticação da API do GitHub for necessária durante a instalação, passe o token via ENV (mesmo padrão de evolution):

GITHUB_PAT=ghp_xxx make demo-all

Nomes de ENV alternativos também são suportados: GITHUB_TOKEN, GH_TOKEN.

Exemplos manuais de MCP de leitura de conteúdo (mesmas chamadas usadas em demo/logs.md):

# list tools
curl -sS -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>' \
  -d '{"jsonrpc":"2.0","id":"tools-1","method":"tools/list","params":{}}' \
  'http://127.0.0.1:8787/api/v1/mcp/content'

# read content slice from DB
curl -sS -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>' \
  -d '{"jsonrpc":"2.0","id":"search-1","method":"tools/call","params":{"name":"evo.content.search","arguments":{"limit":3,"offset":0}}}' \
  'http://127.0.0.1:8787/api/v1/mcp/content'

# read one document
curl -sS -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>' \
  -d '{"jsonrpc":"2.0","id":"get-1","method":"tools/call","params":{"name":"evo.content.get","arguments":{"id":1}}}' \
  'http://127.0.0.1:8787/api/v1/mcp/content'

Verificação opcional de integração de runtime (contra ambiente implantado):

EMCP_INTEGRATION_ENABLED=1 \
EMCP_BASE_URL="https://example.org" \
EMCP_API_PATH="/api/v1/mcp/{server}" \
EMCP_API_TOKEN="<jwt>" \
EMCP_SERVER_HANDLE="content" \
EMCP_DISPATCH_CHECK=1 \
composer run test:integration:runtime

Nota de release do CI:

  • .github/workflows/ci.yml executa demo-runtime-proof, runtime-integration e migration-matrix (sqlite/mysql/pgsql) em pushes release/*.
  • Configure a proteção de branch para tornar esses jobs obrigatórios para merges de RC/release.

Assíncrono (sTask primeiro)

Se queue.driver=stask e sTask estiverem instalados, o eMCP pode executar chamadas MCP longas via worker emcp_dispatch. Se o sTask estiver ausente, o comportamento de fallback segue queue.failover (sync ou fail).

Notas de Segurança

  • Mantenha segredos em .env ou core/custom/config/*.
  • Logs de auditoria devem redigir tokens/segredos.
  • Use denylist de ferramentas e allowlist de servidores para endurecimento em produção.

Padrões de Segurança

  • negação por padrão para manager/API sem acesso explícito.
  • security.enable_write_tools=false por padrão.
  • redação de chaves sensíveis em logs é obrigatória.
  • verificações de escopo da API (mcp:read|call|admin) são obrigatórias no Portão B+.
  • limites de depth/limit/payload devem permanecer habilitados.

Checklist de release de segurança: SECURITY_CHECKLIST.md. Modelo de ameaças: THREAT_MODEL.md. Congelamento de arquitetura: ARCHITECTURE_FREEZE_CHECKLIST.md.

Licença

MIT (LICENSE).