Equipo Easybits
6 min de lectura
agentes
Tienes una app con su API y quieres que un agente de la flota ejecute acciones de esa app: crear una orden, buscar un cliente, cancelar una cita. Este tutorial cubre el patrón completo: qué expone tu app, cómo lo registras en el agente y cómo compruebas que está vivo.
La app es dueña de sus tools. El agente no sabe nada de la app. Al inicio de cada turno, la flota le conecta al agente un servidor MCP que expone tus tools. Ese servidor le pega a tu API con una llave; tu app decide qué tool existe, quién puede llamarla y cuáles esperan aprobación humana. El modelo nunca arma llamadas HTTP a mano.
Tres capas:
| Capa | Dónde vive | Qué hace |
|---|---|---|
| Flota | EasyBits, FleetAgent | Recibe el turno, levanta un worker y le pasa los MCPs habilitados para ese grupo |
| MCP | Un endpoint en tu app | Traduce tools/list y tools/call en llamadas a tu API con la llave |
| App | Tu backend | Catálogo, despacho, auth por llave, gate de confirmación |
Un endpoint Streamable-HTTP en tu app, por ejemplo https://miapp.com/api/mcp, que:
Authorization: Bearer <llave> y con esa llave resuelva tenant y permisos.tools/list y tools/call.Es lo mismo que EasyBits sirve en su propio /api/mcp. Con el SDK oficial de MCP para tu lenguaje sale en una tarde. HTTP es el transporte remoto estándar del protocolo y no requiere publicar nada en npm ni instalar nada en el worker. La alternativa stdio (un paquete npm) existe, pero obliga a descargar el paquete en cada turno dentro de la microVM.
Un esqueleto mínimo en Node:
Todo va contra un solo endpoint, autenticado con el token del agente:
Con el SDK:
En el siguiente turno el agente ya trae mcp__miapp__*.
Las dos últimas llamadas son distintas y las dos hacen falta. add-mcp mete tu MCP al menú del agente. set-cap-level lo habilita para un grupo. El worker sólo recibe lo que está habilitado; si tu nombre no está ahí, el turno corre sin tus tools y sin error. Si quieres encenderlo sólo para un canal, usa su groupId en lugar de "*".
Tu MCP debe salir en capabilities[] con secretsPresent: true y en groups["*"].mcpServers. Si falta el secret, la entrada se omite en silencio.
Después manda un turno con un groupId nuevo y pregunta "¿qué herramientas tienes?":
El groupId nuevo importa: la sesión de una conversación fija sus MCPs al arrancar, y probar sobre una conversación vieja puede mostrar la config anterior.
tools/list devuelva las de uso diario más un discover_tools({query}) y un run_tool({name, params}); tools/call acepta todas. Con 70 tools planas el modelo niega capacidades que sí tiene.needs_confirmation no es error. Para acciones que esperan aprobación humana devuelve un resultado normal, sin isError. Marcado como error, el modelo reintenta.El enforcement es estructural, no un prompt. "Pide permiso antes de cancelar" se cumple casi siempre, y el "casi" es una cita cancelada de verdad.
needs_confirmation y archiva el payload en una tabla de pendientes. No existe ninguna tool que acepte un token de confirmación: el modelo no tiene por dónde proceder.Un FleetAgent por tenant, cada uno con su vault y su llave. El aislamiento es estructural y funciona sin pedir nada.
Lo que no hay que hacer nunca: resolver el tenant desde el texto del prompt. Un tenantId que el MCP "confía" porque venía en appendSystemPrompt es inyección de prompt desde el navegador del cliente.
/api/mcp Streamable-HTTP con Bearer en tu app.set-secret → add-mcp { url } → set-cap-level { groupId: "*" }.GET /capabilities: secretsPresent: true y tu nombre en mcpServers.groupId nuevo: "¿qué herramientas tienes?".Foto de Magda Ehlers en Pexels.