Configurar un MCP en AdonisJS (o cómo darle llaves de tu casa a una IA)
Post de prueba: cómo montar un servidor MCP dentro de AdonisJS para que tu IA favorita pueda hablar con tu app sin que tengas que copiar y pegar como un monje medieval.

¿Qué es eso de MCP y por qué debería importarme?
MCP (Model Context Protocol) es básicamente el USB-C de las IAs: un estándar para que un modelo como Claude se conecte a tus herramientas, datos y servicios sin que tengas que inventar una integración distinta para cada cosa. Tú expones "tools" (funciones), la IA las descubre y las usa. Fin. Sin magia negra, solo JSON-RPC con buena actitud.
Y si ya tienes una app en AdonisJS, ¿por qué no meter el servidor MCP ahí mismo? Ya tienes modelos, auth, rutas... solo falta enseñarle a la IA a tocar el timbre.
Paso 1: Instalar lo necesario
npm i @modelcontextprotocol/sdk zod
Zod viene de regalo porque el SDK lo usa para describir los parámetros de las tools. Sí, otra dependencia. No, no vamos a hablar del tamaño de node_modules, eso es un tema de terapia.
Paso 2: Crear el servidor MCP
Yo lo dejo en app/mcp/server.ts, pero ponlo donde tu corazón (y tu linter) te lo permitan:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { z } from 'zod'
export function buildMcpServer() {
const server = new McpServer({ name: 'mi-blog', version: '1.0.0' })
server.registerTool(
'saludar',
{
description: 'Saluda a alguien con mucho entusiasmo',
inputSchema: { nombre: z.string() },
},
async ({ nombre }) => ({
content: [{ type: 'text', text: `¡Hola ${nombre}! Te saluda tu servidor Adonis favorito 🎉` }],
})
)
return server
}
Aquí podrías registrar tools que consulten tus modelos de Lucid, creen posts, borren comentarios de spam... lo que quieras. Con gran poder viene gran responsabilidad, Peter.
Paso 3: El controlador
Usamos el transporte HTTP "streamable" del SDK y le pasamos los objetos crudos de Node que Adonis tiene guardados por ahí:
import type { HttpContext } from '@adonisjs/core/http'
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
import { buildMcpServer } from '#mcp/server'
export default class McpController {
async handle({ request, response }: HttpContext) {
const server = buildMcpServer()
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })
response.response.on('close', () => {
transport.close()
server.close()
})
await server.connect(transport)
await transport.handleRequest(request.request, response.response, request.body())
}
}
sessionIdGenerator: undefined lo deja en modo stateless: cada request es un servidor nuevecito, sin recuerdos, como yo los lunes. Es lo más simple para empezar.
Ojo: el alias
#mcp/serverlo tienes que agregar en losimportsde tupackage.json, o usar una ruta relativa como un ser humano normal.
Paso 4: La ruta
En start/routes.ts:
const McpController = () => import('#controllers/mcp_controller')
router.post('/mcp', [McpController, 'handle'])
Y si tienes Shield con CSRF activado (deberías), excluye la ruta en config/shield.ts, porque la IA no sabe de tokens CSRF y se va a poner triste:
csrf: {
enabled: true,
exceptRoutes: ['/mcp'],
}
Ya que estás, ponle auth a esa ruta. Un endpoint MCP abierto al mundo es como dejar la puerta de tu casa abierta con un cartel que dice "hay cerveza adentro".
Paso 5: Conectarlo y probar
Levanta tu app con node ace serve --hmr y conéctalo desde Claude Code:
claude mcp add --transport http mi-blog http://localhost:3333/mcp
Ahora pídele a Claude que salude a alguien y mira cómo usa tu tool. Si funciona, haz un bailecito. Si no funciona, revisa la consola, toma café y vuelve a intentar. Ese es el ciclo de la vida del desarrollador.
Conclusión
Montar un MCP en AdonisJS es sorprendentemente sencillo: un servidor, un controlador, una ruta y listo. Lo difícil no es configurarlo, es decidir qué tanto poder le das a la IA sobre tu app sin que termine reescribiendo tu base de datos "para optimizarla".
Como siempre, estamos aprendiendo juntos, así que cualquier comentario es bienvenido. xD