eMCP for Evolution CMS

Otorga a los agentes de IA acceso de lectura y escritura al árbol de documentos, variables de plantilla y elementos, con permisos de administrador, grupos de documentos, bloqueos de elementos y auditoría de manager_log aplicados a cada llamada. Además, generadores artisan para crear tus propios servidores y herramientas MCP dentro de Evo.

Documentación

Total Downloads Latest Stable Version License

eMCP para Evolution CMS

eMCP es la capa de integración de Evolution CMS para laravel/mcp.

Adapta Laravel MCP al runtime de Evo con:

  • Publicación de configuración nativa de Evo
  • Controles de ACL del administrador y ámbito de sApi
  • Despacho asíncrono opcional a través de sTask
  • Sin requisito de esqueleto de aplicación Laravel
  • Herramientas MCP de dominio Evo para el árbol de documentos (SiteContent + TVs)

La implementación comienza con una puerta MVP estricta:

  • Transporte web
  • Modo administrador
  • initialize + tools/list

Estilo de diseño:

  • Primero el contrato (TOOLSET.md + validadores)
  • Registro de servidores declarativo y basado en configuración (config/mcp.php)
  • Pipeline de manejadores explícito (validate -> authorize -> query -> map -> paginate)

Si necesitas la arquitectura completa y los contratos, consulta DOCS.md (EN) o DOCS.uk.md (UA). Contrato canónico público de herramientas: TOOLSET.md. Política de versionado y BC: PRD.md (sección API Stability Policy). Manual de operaciones: OPERATIONS.md.

Requisitos

  • Evolution CMS 3.5.2+
  • PHP 8.3+
  • Composer 2.2+
  • seiger/sapi 1.x (instalado como dependencia; solo se usa cuando auth.mode = sapi_jwt)
  • seiger/stask 1.x (instalado como dependencia)

Instalación

Desde tu directorio core de Evo:

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

Publicar Configuración y 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

Archivos publicados:

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

Inicio Rápido (Interno + Externo)

El contrato predeterminado es agnóstico al concepto y sigue el comportamiento de Laravel MCP en primer lugar.

  1. Crea tus clases de servidor/herramienta MCP:
php artisan make:mcp-server ContentServer
php artisan make:mcp-tool HealthTool

Las clases generadas se colocan en core/custom/app/Mcp/....

  1. Registra el servidor en core/custom/config/mcp.php (servers[]).
  2. Prueba la ruta interna/administrador:
  • POST /{manager_prefix}/{handle} con sesión de administrador y permiso emcp.
  1. Habilita el modo API externo (si sApi está instalado):
  • mantén mode.api=true en core/custom/config/cms/settings/eMCP.php
  • llama a POST /{SAPI_BASE_PATH}/{SAPI_VERSION}/mcp/{handle} con Bearer JWT y los ámbitos mcp:* requeridos.
  • obtén el JWT desde POST /{SAPI_BASE_PATH}/{SAPI_VERSION}/token (endpoint de token sApi).
  1. Asíncrono opcional:
  • establece queue.driver=stask, asegúrate de que sTask esté instalado, usa el endpoint de despacho para trabajos de larga duración.

Tokens de acceso personal (autenticación API predeterminada)

Desde esta versión, el endpoint de API no necesita paquetes adicionales: un usuario administrador crea un token de acceso personal y cada solicitud se ejecuta como ese usuario, con el rol, los permisos y los grupos de documentos de ese usuario — exactamente como si estuvieran conectados al administrador.

  1. Otorga al rol el permiso emcp (los administradores lo tienen después de migrate).
  2. Abre Herramientas → Tokens MCP en el administrador ({manager_url}/emcp/tokens), o ejecuta php artisan emcp:token:create <username> --scopes=mcp:read,mcp:call --expires=90.
  3. Conecta el 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"

Los ámbitos restringen un token, nunca amplían al usuario: mcp:read (listar/leer), mcp:call (herramientas de solo lectura), mcp:write (herramientas evo.write.*, también controladas por security.enable_write_tools), mcp:admin. Los tokens se almacenan con hash, pueden expirar y se revocan desde la misma página o con emcp:token:revoke. emcp:token:list muestra lo que existe.

auth.mode en core/custom/config/cms/settings/eMCP.php selecciona pat (predeterminado), sapi_jwt (JWT de seiger/sapi, ahora también suplantando al usuario del JWT) o none.

Herramientas de escritura

evo.write.content.update|create|publish, evo.write.elements.save, evo.write.cache.clear más las herramientas de lectura evo.elements.list|get. Cada una vuelve a verificar el permiso de administrador de la acción de administrador correspondiente (save_document, publish_document, save_chunk, new_snippet, ...), el acceso a grupos de documentos, los bloqueos de elementos, dispara los mismos eventos OnBefore*FormSave/On*FormSave y escribe una fila manager_log, para que un administrador vea los cambios de API junto a los del navegador.

Herramientas de otros extras

Un extra contribuye con herramientas implementando EvolutionCMS\eMCP\Contracts\ToolProvider y registrándolo en el boot() de su proveedor de servicios:

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

Las herramientas son clases Laravel\Mcp\Server\Tool simples; marca las que cambian el sitio con EvolutionCMS\eMCP\Contracts\WritesSite para que security.enable_write_tools y el ámbito mcp:write se apliquen a ellas. El código de la herramienta se ejecuta como el administrador suplantado, por lo que evo()->hasPermission() y las propias protecciones del extra se comportan como en la página. Alternativamente, lista las clases bajo mcp.servers[].extra_tools. Ejemplo: elcreator/aimage incluye herramientas aimage.* de esta manera.

Pruébalo en 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

Filosofía de Diseño (Lectura Opcional)

Por Qué Existe Este Producto (4 Preguntas Centrales, Aristóteles)

Esta es la forma más corta de entender eMCP como producto, no solo como paquete.

  1. Causa material: de qué consiste (límites duros):
  • protocolo/runtime de laravel/mcp
  • capa adaptadora de Evo (ServiceProvider, registro, rutas, middleware, publicación)
  • integraciones opcionales de acceso/asíncrono (sApi, sTask)
  • contratos canónicos (SPEC.md, TOOLSET.md)
  1. Causa formal: qué forma lo convierte en producto (no componentes):
  • un contrato de ejecución desde la solicitud hasta la respuesta auditada
  • un modelo de política para acceso administrador/API (ACL + scopes + limits)
  • un contrato público de herramientas versionado para consumidores del ecosistema
  • un modelo de extensión estable para paquetes de terceros
  1. Causa eficiente: qué lo pone en movimiento (flujos de trabajo + disparadores):
  • disparador interno: llamada MCP del administrador (/{manager_prefix}/{handle})
  • disparador externo: llamada MCP de API (/{SAPI_BASE_PATH}/{SAPI_VERSION}/mcp/{handle})
  • disparador asíncrono: despacho al trabajador sTask para operaciones largas
  • disparador de ciclo de vida: instalación/publicación/registro/prueba del paquete
  1. Causa final: por qué está construido de esta manera:
  • mantener intacta la semántica de Laravel MCP
  • mantener la integración de Evo explícita y operable
  • soportar uso MCP interno y externo
  • permitir múltiples estrategias de orquestación sobre una base MCP neutral

Modelo Conceptual (Lente de Diseño)

Esta lente ayuda a explicar las decisiones de arquitectura:

  • teoría de conjuntos: los datos del CMS son conjuntos estructurados (sitio -> nodos -> atributos)
  • secuencia de Peano: los flujos de trabajo son transiciones de estado ordenadas
  • límites de Godel: los sistemas de reglas autorreferenciales necesitan límites estrictos

Implicación práctica:

  • eMCP permanece como capa de contrato/runtime
  • la lógica de orquestación permanece en los paquetes consumidores
  • las puertas de política/auditoría/humanas evitan que los bucles de reglas recursivas se vuelvan inseguros

Verificación de Instalación (1 minuto)

Para la Puerta A, usa el endpoint de administrador /{manager_prefix}/{server_handle} (predeterminado: /emcp/content). La Puerta A está protegida por ACL del administrador, así que ejecuta las comprobaciones como administrador conectado con permiso emcp (se requiere cookie de sesión).

  1. Verifica que GET devuelva 405 en el endpoint MCP:
curl -i -X GET http://localhost/<MANAGER_PREFIX>/<SERVER_HANDLE> \
  -H 'Cookie: evo_session=<MANAGER_SESSION_COOKIE>'
  1. Verifica 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 en los encabezados de respuesta.
  • HTTP 405 estable en GET.

Registrar Servidores MCP (estilo Evo)

A diferencia del routes/ai.php predeterminado de Laravel, eMCP registra servidores desde configuración.

Ejemplo en 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:

  • El endpoint de administrador de la Puerta A sigue siendo /{manager_prefix}/{handle} (por ejemplo /emcp/content).
  • servers[*].route es usado por el registro de transporte web y se vuelve relevante externamente en modo API (Puerta B+).
  • content-local está deshabilitado por defecto para evitar conflictos de registro de nombres de herramientas duplicados con content.

Modelo de Acceso

  • Acceso administrador/interno: permiso de Evo emcp
  • Acceso API (vía sApi): ámbitos JWT (mcp:read, mcp:call, mcp:admin)
  • Lecturas de dominio (evo.content.*, evo.model.*) son de solo lectura por defecto

Interoperabilidad del Ecosistema

eMCP es la capa de plataforma MCP para el ecosistema Evo:

  • LaravelMcp: contrato de protocolo/runtime ascendente (mantenido intacto).
  • sApi: kernel de API externa + descubrimiento de proveedores de rutas JWT.
  • sTask: ejecución de trabajos/tareas asíncronas y progreso.
  • eAi: el runtime de IA puede llamar herramientas MCP a través del modo administrador o API.
  • dAi: la UI de orquestación del lado del administrador puede consumir herramientas eMCP como contrato estable.

Esto mantiene el núcleo declarativo y neutral: una base MCP para múltiples conceptos de orquestación.

Herramientas de Dominio Evo

  • Implementadas ahora: evo.content.search|get|root_tree|descendants|ancestors|children|siblings
  • Opcionales (implementadas): evo.content.neighbors|prev_siblings|next_siblings|children_range|siblings_range
  • Consultas conscientes de TV vía with_tvs, tv_filters, tv_order estructurados
  • evo.model.list|get implementado con proyección de lista blanca explícita por modelo y lista negra de defensa en profundidad para campos sensibles

Comandos Artisan

De Laravel MCP (disponibles vía 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, primero habilita content-local en core/custom/config/mcp.php y deshabilita entradas de servidor conflictivas si exponen nombres de herramientas idénticos.

Servidores de terceros

Registra herramientas específicas del proyecto en una clase Laravel\Mcp\Server separada y agrega esa clase como su propia entrada en core/custom/config/mcp.php. Los nombres de herramientas de terceros no deben usar el espacio de nombres reservado evo.*. No subclases el ContentServer del paquete solo para agregar herramientas del proyecto, porque el conjunto de herramientas evo.* heredado es propiedad del paquete y el registro rechazará ese servidor externo.

php artisan emcp:test --server=<handle> ejecuta comprobaciones genéricas initialize y tools/list para servidores de terceros. Cuando la clase seleccionada es el ContentServer canónico de eMCP, también verifica el conjunto de herramientas de Evolution requerido. Usa php artisan emcp:list-servers para ver tanto los servidores aceptados como las razones concretas por las que las entradas configuradas fueron rechazadas.

Comandos operativos de 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

Comprobaciones del Repositorio (para primera ejecución en el espacio de trabajo del paquete)

Si estás validando este repositorio directamente:

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

Estas comprobaciones validan composer.json y ejecutan lint de sintaxis PHP en las fuentes del paquete.

Demo de un clic + verificación MCP completa:

make demo-all

Este objetivo instala Evo demo, inicia php -S, emite sApi JWT, ejecuta php artisan emcp:test, luego ejecuta composer run test con integración de runtime HTTP habilitada. Después de la ejecución, la evidencia detallada se escribe en:

  • demo/logs.md (información de autenticación enmascarada/token, cargas útiles de solicitud MCP, estados HTTP, respuestas, comandos de verificación manual, más sondas negativas: 401/403/413/415/409/429 y verificación de sanidad evo.model.get(User))
  • demo/logs.md también incluye prueba de ciclo de vida local sTask (queued -> completed) vía php artisan stask:worker en el runtime demo.
  • /tmp/emcp-demo-php-server.log (log del servidor integrado de PHP)

Si se necesita autenticación de API de GitHub durante la instalación, pasa el token vía ENV (mismo patrón que evolution):

GITHUB_PAT=ghp_xxx make demo-all

También se admiten nombres ENV alternativos: GITHUB_TOKEN, GH_TOKEN.

Ejemplos manuales de MCP de lectura de contenido (mismas llamadas usadas en 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'

Comprobación opcional de integración de runtime (contra entorno desplegado):

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 de CI:

  • .github/workflows/ci.yml ejecuta demo-runtime-proof, runtime-integration y migration-matrix (sqlite/mysql/pgsql) en pushes release/*.
  • Configura protección de rama para hacer estos trabajos obligatorios para merges de RC/release.

Asíncrono (sTask primero)

Si queue.driver=stask y sTask están instalados, eMCP puede ejecutar llamadas MCP largas vía trabajador emcp_dispatch. Si falta sTask, el comportamiento de respaldo sigue queue.failover (sync o fail).

Notas de Seguridad

  • Mantén los secretos en .env o core/custom/config/*.
  • Los logs de auditoría deben redactar tokens/secretos.
  • Usa lista negra de herramientas y lista blanca de servidores para endurecer producción.

Valores Predeterminados de Seguridad

  • denegar por defecto para administrador/API sin acceso explícito.
  • security.enable_write_tools=false por defecto.
  • La redacción de claves sensibles en logs es obligatoria.
  • Las comprobaciones de ámbito de API (mcp:read|call|admin) son requeridas en la Puerta B+.
  • Los límites de depth/limit/payload deben permanecer habilitados.

Lista de verificación de seguridad de release: SECURITY_CHECKLIST.md. Modelo de amenazas: THREAT_MODEL.md. Congelación de arquitectura: ARCHITECTURE_FREEZE_CHECKLIST.md.

Licencia

MIT (LICENSE).