symfony-command-ui: tus comandos Symfony son tu MCP server
El problema que todos tenemos
Si trabajas con Symfony, tienes comandos de consola. Muchos.
app:users:sync, app:payments:process,
app:cache:warmup… Son la columna vertebral de tu
aplicacion: encapsulan logica de negocio, se ejecutan en cron, los usas
para debuggear, para migraciones, para todo.
Y sin embargo, ejecutarlos es un rollo:
- SSH al servidor (o al pod de Kubernetes)
- Recordar la sintaxis exacta (
--limit=100o--limit 100?) - Copiar el output a mano si quieres compartirlo
- Si estas en un pod de K8s, ni siquiera tienes terminal a veces
Y ahora, en la era agentica, hay un segundo problema: como le dices a un agente de IA que ejecute tus comandos? Los LLMs son buenos entendiendo JSON, haciendo HTTP, procesando streams. Pero no pueden hacer SSH a tu pod.
La solucion: un bundle que los expone a todos
symfony-command-ui es un bundle de Symfony que he
publicado hoy. Hace una cosa simple:
Convierte tus comandos de consola en una UI web con terminal streaming Y en una API JSON que cualquier agente de IA puede usar.
composer require pascualmg/symfony-command-uiTres lineas de YAML y tienes un dashboard con todos tus comandos,
auto-descubiertos, con formularios generados automaticamente desde tu
InputDefinition.
Como funciona
La arquitectura es simple:
- El bundle ejecuta
php bin/console list --format=jsonpara descubrir los comandos disponibles - Filtra por una whitelist que tu configuras (seguridad)
- Traduce cada
InputOptionyInputArgumenta un formulario web (checkboxes, dropdowns, text inputs) - Cuando pulsas Run, ejecuta el comando via
Symfony\Component\Processy te streama el output en tiempo real como NDJSON
Cada comando se renderiza como una card independiente con su propio formulario y su propia terminal. Los outputs persisten: puedes ejecutar Stats mientras Generate JWT mantiene su resultado.
La parte interesante: interfaz dual
Aqui es donde se pone bueno. El bundle expone dos interfaces con los mismos endpoints:
Para humanos: el dashboard web
Un Web Component (<symfony-command>) con Shadow
DOM, zero dependencias, que descubre los comandos al montar y renderiza
todo automaticamente. No hay npm, no hay webpack, no hay build step. Un
unico fichero JS que el bundle sirve como asset.
CSS customizable via custom properties. Dark theme por defecto. Theming completo si quieres.
Para agentes: la API JSON
Dos endpoints:
GET /commands: devuelve un JSON estructurado con todos los comandos disponibles, sus opciones, defaults y tiposPOST /execute: recibe{command, options}y streama el output como NDJSON
Cualquier agente que hable HTTP puede:
- Descubrir que operaciones existen en tu app
- Entender sus parametros (sin documentacion extra – salen del
InputDefinitionde Symfony) - Ejecutarlas y observar el resultado en tiempo real
Es un MCP server gratis. Sin instalar nada especial. Sin protocolo custom. Solo HTTP + JSON.
Un ejemplo real: Claude operando una app
Imagina que tienes un e-commerce con estos comandos:
allowed_commands:
- app:orders:pending
- app:orders:process
- app:inventory:check
- app:reports:dailyUn agente de IA (Claude, GPT, tu propio agente) puede hacer:
Tu: "Hay pedidos pendientes de mas de 500 euros?"
Agente: /llama GET commands, encuentra app:orders:pending /llama POST execute con {"command":"app:orders:pending","options":{"–min-amount":500,"–json":true}}
"Si, hay 3 pedidos pendientes por encima de 500 euros: #4521 (720 euros), #4523 (1100 euros), #4529 (550 euros). Quieres que los procese?"
Tu: "Primero con dry-run"
Agente: /llama POST execute con {"command":"app:orders:process","options":{"–ids":"4521,4523,4529","–dry-run":true}}
"Dry run completado. Los 3 se procesarian correctamente. Total: 2370 euros. Ejecuto en real?"
Todo esto funciona porque tus comandos de Symfony ya encapsulan la logica. El bundle solo la expone.
El patron: logica en comandos, interfaz como adaptador
Si haces DDD o hexagonal, tus comandos de Symfony son adaptadores CLI que llaman a use cases del dominio. Este bundle anade un segundo adaptador (HTTP) al mismo use case, sin tocar una linea de tu logica de negocio.
┌──────────────┐
bin/console──►│ │
│ Use Case │
symfony-command-ui──►│ (dominio) │
(HTTP+NDJSON) │ │
└──────────────┘
No importa si llegas por CLI o por HTTP: el mismo codigo se ejecuta. El bundle solo se encarga del transporte.
Detalles tecnicos
- PHP >= 7.4, Symfony 5.4 / 6.x / 7.x
- NDJSON sobre HTTP (no WebSocket, no SSE).
fetch()+ReadableStream+TextDecoder - Web Component con Shadow DOM. Un unico fichero JS, zero deps
- Auto-discovery via
bin/console list --format=json+Process - Whitelist configurable. Solo los comandos que tu permitas se exponen
- Overrides para convertir text inputs en dropdowns
(ej:
--gateway: [stripe, paypal, braintree]) - No incluye autenticacion. Tu pones tu
access_control, firewall, VPN o lo que uses
Instalacion en 4 pasos
# 1. Instalar
composer require pascualmg/symfony-command-ui
# 2. El bundle se auto-registra en bundles.php
# 3. Importar rutas
# config/routes/symfony_command_ui.yaml
# symfony_command_ui:
# resource: '@SymfonyCommandUIBundle/Resources/config/routes.php'
# prefix: /symfony-console
# 4. Configurar whitelist
# config/packages/symfony_command_ui.yaml
# symfony_command_ui:
# allowed_commands:
# - app:mi:comando
# - app:otro:comandoAbrir https://tu-app.com/symfony-console y listo.
El origen
Esto surgio de un problema real: teniamos un microservicio de identity con 6 comandos de consola para gestionar colectivos (una feature B2B para un partner). Necesitabamos una UI para que el equipo pudiera ejecutarlos sin SSH, y al mismo tiempo queriamos que los agentes de IA pudieran operar el flujo programaticamente.
En vez de hacer un dashboard ad-hoc, extraje la logica generica en un bundle reutilizable. El prototipo funciono en una tarde. La lib se publico al dia siguiente. Hoy esta en Packagist.
Links
- Repositorio: https://github.com/pascualmg/symfony-command-ui
- Packagist: https://packagist.org/packages/pascualmg/symfony-command-ui
- Licencia: MIT
Comentarios (0)
Sin comentarios todavia. Se el primero!
Deja un comentario