Una Sola Puerta para Todos los Agentes: Cómo Expongo ProjectHub vía MCP
Construí memoria, proyectos y comunicación entre agentes detrás de una API REST. Pero uso seis herramientas de IA distintas, y escribir y mantener una integración para cada una era volver al problema de la fragmentación, un nivel más arriba. Así expuse todo ProjectHub a través de un solo protocolo —MCP— para que cualquier agente, sin glue a la medida, tenga memoria, tareas y canales como herramientas nativas.
Una Sola Puerta para Todos los Agentes: Cómo Expongo ProjectHub vía MCP
Para cuando terminé de construir la memoria, las tareas y la comunicación entre agentes, tenía un backend sólido: una API REST en Laravel con todo bien resuelto. Y un problema nuevo, que era el viejo disfrazado.
Uso seis herramientas de IA distintas —Claude Code, Cursor, Codex, OpenClaw, opencode, Copilot—. Cada una llama a herramientas externas a su manera. ¿Iba a escribir una integración a la medida para cada una? ¿Y mantener seis pegamentos distintos cada vez que agregara un endpoint? Era exactamente la fragmentación que tanto me molestaba en la memoria —tener que repetir lo mismo por cada agente—, pero un nivel más arriba: ahora tendría que reintegrar lo mismo por cada herramienta.
Este post es sobre cómo lo resolví con una sola decisión: no escribir integraciones, sino hablar un protocolo. El protocolo es MCP, y convirtió a ProjectHub en una puerta única por la que entra cualquier agente.
El Problema N×M
Es un clásico de integración. Tienes N clientes (mis seis herramientas) y M capacidades (memoria, tareas, canales). Si conectas cada cliente con cada capacidad a mano, terminas con N×M integraciones que mantener. Agregas una herramienta nueva: M integraciones más. Agregas una capacidad nueva: N integraciones más. No escala —es la misma trampa de los silos, multiplicada—.
La salida a un problema N×M nunca es hacer las N×M conexiones más rápido. Es meter un estándar en medio: que los N clientes y las M capacidades hablen el mismo idioma, y entonces son N+M, no N×M.
MCP: un Protocolo, No una Integración
Ese idioma común es MCP —Model Context Protocol—. La forma fácil de entenderlo: es el USB-C de las herramientas para agentes. Cualquier cliente que hable MCP puede conectarse a cualquier servidor MCP y descubrir, automáticamente, qué herramientas ofrece y cómo llamarlas. No hay que programar la integración en el cliente: el cliente ya sabe hablar MCP, y mi servidor ya sabe responderlo.
Así que no escribí seis integraciones. Escribí un servidor MCP —en TypeScript— que se para enfrente de la API REST de ProjectHub y expone sus capacidades como herramientas MCP. Claude Code, Cursor y cualquier otro cliente compatible las ven como si fueran nativas. Agregar una herramienta nueva la hace visible para todos a la vez; conectar una herramienta nueva no cuesta código mío.
Una Sola Puerta para Tres Mundos
Por esa puerta entran las tres capas que construí, como un solo catálogo de herramientas:
- Memoria:
memory_store,memory_search,memory_get,memory_list… (guardar, recuperar por significado, revelar secretos con auditoría). - Proyectos:
task_create,task_update,task_list,comment_add… (el trabajo en vuelo, con estados y comentarios tipados). - Canales: las ~13 tools de Agent Channels —
agent_directory,comms_open/close,link_request/accept,agent_send,agent_inbox,ack…— para la comunicación en tiempo real entre agentes.
Internamente cada tool mapea un endpoint /api/v1/* de la API REST. El servidor MCP es una capa fina de traducción: recibe la llamada MCP, pega al REST, devuelve el resultado en el formato que el protocolo espera.
Las Tools se Enseñan a Usarse Solas
Este detalle es el que más me gustó, porque cierra el círculo con el que abrí toda esta historia. En la descripción de cada tool —el texto que el agente lee para decidir si la usa— metí el protocolo, no solo la firma. La tool agent_inbox no dice solo "trae mensajes": explica que hay que sondearla en long-poll con wait=25 y que sondear es lo que mantiene vivo el enlace. Las de Channels enseñan el handshake completo.
El efecto: el agente aprende a usar el sistema leyendo las herramientas, no leyéndome a mí. Es la misma idea que me llevó a construir la memoria —dejar de reexplicar lo mismo—, ahora aplicada al propio tooling. La documentación del cómo viaja dentro de la herramienta.
Stateless por Diseño
El servidor corre en modo HTTP, y tomé una decisión que parece rara hasta que la piensas: es stateless. Por cada request crea un servidor MCP nuevo con un transporte StreamableHTTPServerTransport sin generador de sesión, lo conecta, responde y lo descarta. No guarda conexiones vivas por agente.
// por request: instancia efímera, sin sesión persistente
const server = buildServer(); // registerAllTools(server)
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
await server.connect(transport);
await transport.handleRequest(req, res); // responde y se descarta¿Por qué? Porque es robusto y barato: no hay sesiones que se queden colgadas, no hay estado que limpiar, reiniciar el proceso no tira a nadie, y escala horizontal sin pegamento. El estado durable ya vive donde debe —en la base de datos de ProjectHub—, no en el servidor MCP. La puerta no necesita recordar nada; solo traducir.
Cuando Hace Falta Estado: /mcp/live
El modelo stateless tiene un límite: no puede empujar nada al cliente, porque no hay conexión viva. Para los runtimes que sí reaccionan a mitad de sesión —y quieren recibir un mensaje de otro agente sin sondear— monté un endpoint separado, /mcp/live, con sesión persistente y SSE, donde el inbox es un recurso suscribible (projecthub://inbox): el cliente se suscribe y el servidor emite notifications/resources/updated cuando llega algo, conectado al mismo LISTEN/NOTIFY de Postgres del que hablé en el post de canales.
La clave del diseño: /mcp/live se montó al lado, sin tocar el /mcp stateless. Quien quiere simplicidad usa el stateless con long-poll; quien quiere push usa el stateful. Dos puertas para dos necesidades, sin que una rompa la otra.
La Trampa de las Herramientas Nuevas
Un gotcha operativo que pagué con confusión: cuando agregas o cambias tools en el servidor, los clientes MCP no las ven hasta reconectar. El cliente descubre el catálogo de tools al iniciar la sesión y lo cachea. Así que tras un deploy del servidor, hay que reiniciar la sesión del cliente (en Claude Code, recargar la conexión MCP) o seguirás llamando al catálogo viejo. Obvio en retrospectiva; no tan obvio a las once de la noche preguntándote por qué tu tool nueva "no existe".
El Código y Cómo Conectarte
El servidor MCP vive en github.com/andyeswong/projecthub-mcp (TypeScript), enfrente del backend github.com/andyeswong/agentProjectHub (Laravel). Se despliega con pm2:
cd projecthub-mcp
git pull && npm install && npm run build
pm2 restart projecthub-mcp # sirve build/index.http.js en :3000Y conectar un cliente es declarar el servidor —por ejemplo, en la config MCP de tu agente— con tu API key de la organización:
{
"mcpServers": {
"projecthub": {
"url": "https://projecthub.agenthys.com/mcp",
"headers": { "Authorization": "Bearer <tu-api-key>" }
}
}
}A partir de ahí, ese agente tiene memoria, tareas y canales. Sin que yo escriba una línea de integración para su herramienta.
Conclusión
Esta serie fue, en el fondo, una sola pelea repetida en cuatro frentes: dejar de repetirme. Dejé de reexplicarle contexto a cada agente con la memoria. Dejé de quedarme ciego al delegar con las tareas. Dejé de ser el cable entre agentes con los canales. Y con MCP dejé de reintegrar lo mismo con cada herramienta.
MCP es la puerta que vuelve disponible todo lo demás. No le agrega inteligencia al sistema —la inteligencia la pone el modelo, que sigue siendo intercambiable—; le agrega alcance: cualquier agente que hable el protocolo entra y opera como si ProjectHub fuera parte nativa de su herramienta. El modelo es el motor. La memoria, las tareas y los canales son el producto. Y MCP es el enchufe estándar que conecta uno con el otro, sin importar de qué marca sea el motor que tengas hoy enfrente.