Skip to main content

Auto-generación de herramientas MCP

Cuando creas una función con define(), se expone automáticamente como una herramienta MCP (Model Context Protocol). Tus agentes IA pueden descubrir e invocar tu función como un tool sin configuración adicional.

Ejemplo

Para esta función:
index.ts
import { define, z } from "@jelou/functions";

export default define({
  name: "consultar-saldo",
  description: "Consulta el saldo de un cliente por número de teléfono",
  input: z.object({
    telefono: z.string().min(10).describe("Número de teléfono con código de país"),
  }),
  output: z.object({
    nombre: z.string(),
    saldo: z.number(),
    moneda: z.string(),
  }),
  handler: async (input, ctx) => {
    ctx.log("Consultando saldo", { telefono: input.telefono });
    return { nombre: "María García", saldo: 150.00, moneda: "USD" };
  },
});
El endpoint /mcp expone este esquema:
{
  "name": "consultar-saldo",
  "description": "Consulta el saldo de un cliente por número de teléfono",
  "inputSchema": {
    "type": "object",
    "properties": {
      "telefono": {
        "type": "string",
        "minLength": 10,
        "description": "Número de teléfono con código de país"
      }
    },
    "required": ["telefono"]
  }
}

El campo description: lo más importante para MCP

El campo description de tu define() es lo que el agente IA lee para decidir cuándo invocar tu herramienta. Si la descripción es vaga, el agente no sabrá cuándo usarla — o peor, la usará en el momento incorrecto.
❌ Vaga✅ EspecíficaPor qué importa
"Maneja usuarios""Busca un usuario por email o teléfono y retorna su perfil con saldo"El agente sabe exactamente qué datos puede obtener
"Envía mensaje""Envía un mensaje de WhatsApp a un número con código de país (ej: 593…)"El agente sabe el canal y el formato esperado
"Consulta API""Consulta el estado de un envío por tracking number en la API de Servientrega"El agente sabe para qué proveedor y qué dato necesita

Anotaciones .describe() en campos

Las anotaciones .describe() de Zod se convierten en descripciones de parámetros del tool MCP. Esto es lo que ven los agentes IA cuando descubren tu función:
input: z.object({
  telefono: z.string().min(10).describe("Número con código de país, ej: 593987654321"),
  tipo: z.enum(["prepago", "pospago"]).describe("Tipo de plan del cliente"),
  incluirHistorial: z.boolean().default(false).describe("Si incluir las últimas 5 transacciones"),
})
Escribe descripciones claras y específicas en .describe(). Los agentes IA usan estas descripciones para decidir qué valores pasar a tu función.

Probar el endpoint MCP

Inicia el servidor local con jelou dev y consulta el esquema MCP:
curl http://localhost:3000/mcp
Invoca la función directamente:
curl -X POST http://localhost:3000 \
  -H "Content-Type: application/json" \
  -d '{"telefono": "593987654321"}'
En producción:
curl https://consultar-saldo.fn.jelou.ai/mcp

Desactivar MCP

Si tu función no debe ser descubierta como herramienta (por ejemplo, un webhook que solo recibe callbacks), desactiva MCP:
config: { mcp: false }
Cuando MCP está desactivado, el endpoint /mcp retorna 404.

¿Cómo lo usan los agentes?

Cuando configuras un agente IA en Jelou Brain Studio y le asignas funciones como tools, el agente:
  1. Descubre las herramientas disponibles vía el endpoint /mcp
  2. Lee el nombre, descripción y esquema de entrada
  3. Decide cuándo invocar la herramienta basándose en la conversación del usuario
  4. Envía los parámetros validados a tu función
  5. Recibe la respuesta y la incorpora a la conversación
Todo esto sucede automáticamente — solo necesitas escribir la función con define() y asignarla al agente.

Multi-tool MCP

Cuando usas app(), un solo servidor MCP en /mcp registra automáticamente todos los tools. Cada tool aparece como una herramienta independiente con su nombre, descripción y esquema. Para excluir un tool específico del registro MCP, usa mcp: false en su config:
import { app, define, z } from "@jelou/functions";

export default app({
  tools: {
    consultarSaldo: define({
      description: "Consulta el saldo de un cliente",
      input: z.object({ telefono: z.string().min(10) }),
      handler: async (input) => ({ saldo: 150.00 }),
    }),
    webhookPagos: define({
      description: "Recibe callbacks de pagos",
      input: z.object({ transactionId: z.string() }),
      config: { mcp: false },
      handler: async (input) => ({ received: true }),
    }),
  },
});
En este ejemplo, los agentes IA descubren consultarSaldo vía MCP pero webhookPagos solo es accesible por HTTP directo en /webhook-pagos.
Consulta la guía completa de multi-tool para más detalles sobre rutas auto-generadas y combinación de config.