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
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/sapi1.x (instalado como dependência; usado apenas quandoauth.mode = sapi_jwt)seiger/stask1.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.phpcore/custom/config/mcp.phpcore/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.
- 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/....
- Registre o servidor em
core/custom/config/mcp.php(servers[]). - Teste a rota interna/do manager:
POST /{manager_prefix}/{handle}com sessão do manager e permissãoemcp.
- Habilite o modo de API externa (se
sApiestiver instalado):
- mantenha
mode.api=trueemcore/custom/config/cms/settings/eMCP.php - chame
POST /{SAPI_BASE_PATH}/{SAPI_VERSION}/mcp/{handle}com Bearer JWT e os escoposmcp:*necessários. - obtenha o JWT de
POST /{SAPI_BASE_PATH}/{SAPI_VERSION}/token(endpoint de token da sApi).
- Assíncrono opcional:
- defina
queue.driver=stask, garanta quesTaskesteja 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.
- Dê ao papel a permissão
emcp(administradores a têm apósmigrate). - Abra Ferramentas → Tokens MCP no manager (
{manager_url}/emcp/tokens), ou executephp artisan emcp:token:create <username> --scopes=mcp:read,mcp:call --expires=90. - 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.
- 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)
- 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
- 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
sTaskpara operações longas - gatilho de ciclo de vida: instalação/publicação/registro/teste do pacote
- 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).
- Verifique se
GETretorna405no endpoint MCP:
curl -i -X GET http://localhost/<MANAGER_PREFIX>/<SERVER_HANDLE> \
-H 'Cookie: evo_session=<MANAGER_SESSION_COOKIE>'
- 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
200parainitializeválido. MCP-Session-Idpresente nos cabeçalhos da resposta.- HTTP
405estável emGET.
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-localestá desabilitado por padrão para evitar conflito de registro de nomes de ferramentas duplicados comcontent.
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_orderestruturados evo.model.list|getimplementado 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:testphp artisan emcp:list-serversphp artisan emcp:sync-workerscomposer run governance:update-lockcomposer run ci:checkcomposer run benchmark:runcomposer run benchmark:leaderboardcomposer 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 sanidadeevo.model.get(User))demo/logs.mdtambém inclui prova de ciclo de vida localsTask(queued -> completed) viaphp artisan stask:workerno 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.ymlexecutademo-runtime-proof,runtime-integrationemigration-matrix(sqlite/mysql/pgsql) em pushesrelease/*.- 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
.envoucore/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=falsepor 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/payloaddevem 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).