Ir al contenido principal
De OpenAPI a Tool Use: genera schemas de agente en segundos

De OpenAPI a Tool Use: genera schemas de agente en segundos

AI Integration
••6 min read•Por Daily Miranda Pardo

Tu API ya está documentada. El agente no lo sabe.

Tienes una API REST que lleva años funcionando. Documentación OpenAPI completa, 40 endpoints, tipos definidos, schemas de respuesta. Decides conectarla a un agente Claude.

El camino estándar: escribir las tool definitions a mano, una por una, en JSON. Para tres endpoints, dos horas. Para cuarenta, días. Y cuando el equipo de backend actualiza un campo en /orders, nadie recuerda que hay que actualizar también la tool definition del agente. El agente sigue enviando un parámetro que ya no existe, falla silenciosamente, y el bug llega a producción disfrazado de "el agente se confunde".

Hay un camino mejor.

Por qué las tool definitions manuales son una trampa

Una tool definition para Claude tiene esta forma:

const tool: Tool = {
  name: "list_orders",
  description: "Retrieves orders for a customer",
  input_schema: {
    type: "object",
    properties: {
      customer_id: {
        type: "string",
        description: "Customer UUID"
      },
      status: {
        type: "string",
        enum: ["pending", "completed", "cancelled"]
      },
      limit: {
        type: "integer",
        minimum: 1,
        maximum: 100
      }
    },
    required: ["customer_id"]
  }
}

Es tedioso de escribir pero manejable para un endpoint. El problema escala en dos dimensiones:

  • Amplitud: una API de empresa tiene 30, 50, 100 endpoints. Nadie escribe eso a mano
  • Mantenimiento: cuando la API cambia —y siempre cambia—, tienes dos fuentes de verdad que se desincronizaron en silencio

Los equipos que construyen esto solos caen siempre en la misma trampa: exponen cinco endpoints porque escribir los cuarenta restantes "es para después". Ese después nunca llega, y el agente hace el 15% de lo que podría.

Generar tool definitions desde tu OpenAPI spec

Tu OpenAPI spec ya contiene todo lo que Claude necesita: nombre de operación, parámetros, tipos, descripciones, enums, required fields. Solo hay que parsearlo.

import type { OpenAPIV3 } from 'openapi-types'
import type { Tool } from '@anthropic-ai/sdk'

export function openApiToTools(
  spec: OpenAPIV3.Document,
  allowlist?: string[]
): Tool[] {
  const tools: Tool[] = []

  for (const [path, pathItem] of Object.entries(spec.paths ?? {})) {
    const methods = ['get', 'post', 'put', 'delete', 'patch'] as const

    for (const method of methods) {
      const op = (pathItem as OpenAPIV3.PathItemObject)[method] as OpenAPIV3.OperationObject | undefined
      if (!op?.operationId) continue
      if (allowlist && !allowlist.includes(op.operationId)) continue

      const properties: Record<string, object> = {}
      const required: string[] = []

      for (const param of (op.parameters ?? []) as OpenAPIV3.ParameterObject[]) {
        if (!param.name) continue
        if (param.in === 'query' || param.in === 'path') {
          properties[param.name] = param.schema ?? { type: 'string' }
          if (param.required) required.push(param.name)
        }
      }

      const body = op.requestBody as OpenAPIV3.RequestBodyObject | undefined
      const bodySchema = body?.content?.['application/json']?.schema as OpenAPIV3.SchemaObject | undefined
      if (bodySchema?.properties) {
        Object.assign(properties, bodySchema.properties)
        if (bodySchema.required) required.push(...bodySchema.required)
      }

      tools.push({
        name: op.operationId,
        description: op.summary ?? op.description ?? `${method.toUpperCase()} ${path}`,
        input_schema: {
          type: 'object' as const,
          properties,
          ...(required.length > 0 ? { required } : {}),
        },
      })
    }
  }

  return tools
}

Ahora conectar tu API al agente es cuestión de segundos:

import spec from './openapi.json'

const agentTools = openApiToTools(spec, [
  'list_customers',
  'get_order',
  'update_order_status',
])

const response = await anthropic.messages.create({
  model: 'claude-opus-4-5',
  tools: agentTools,
  messages: [{ role: 'user', content: 'Muéstrame los pedidos pendientes del cliente UUID-123' }]
})

Filtra lo que el agente puede hacer

Un agente con acceso completo a tu API puede ejecutar DELETE /customers/all. El parámetro allowlist es el primer filtro, pero en producción necesitas validación también en el ejecutor:

const READ_OPS = new Set(['list_customers', 'get_order', 'get_product_catalog'])

async function executeAgentTool(
  toolName: string,
  toolInput: Record<string, unknown>
): Promise<unknown> {
  if (!READ_OPS.has(toolName)) {
    throw new Error(`Operación no autorizada para este agente: ${toolName}`)
  }
  return callApi(toolName, toolInput)
}

Para validar que el output de la API coincide con lo que el agente espera, añade Zod sobre las respuestas:

import { z } from 'zod'

const OrderSchema = z.object({
  id: z.string().uuid(),
  status: z.enum(['pending', 'completed', 'cancelled']),
  amount: z.number().positive(),
  customer_id: z.string().uuid(),
})

// Si la API cambia y el schema no coincide, Zod lanza antes de que el agente procese datos incorrectos
const order = OrderSchema.parse(apiResponse)

Cómo estructurar esto en producción

La clave es que la spec de OpenAPI sea la única fuente de verdad, compartida entre el backend y el agente:

packages/
  api-spec/
    openapi.json          ← fuente compartida
    generate-tools.ts     ← script de generación
  agent/
    src/
      tools/
        generated.ts      ← output del script (no editar a mano)
        executor.ts       ← validación Zod + llamadas HTTP

El script generate-tools.ts corre en CI antes del build. Si el backend actualiza openapi.json, las tools se regeneran automáticamente. Si hay un mismatch entre la spec generada y los tipos del ejecutor, TypeScript lo detecta antes del deploy.

Es exactamente lo mismo que hacemos en los proyectos de integración IA: tratar la API del cliente como la fuente de verdad y auto-generar la capa de tools en lugar de mantenerla manualmente.

Lo que falla cuando lo construyes solo

Los equipos que llegan a nuestro servicio de agentes IA y automatización después de intentarlo solo tienen el mismo patrón de fallos:

  1. Tools desactualizadas: la API cambió, las tools no, el agente falla un 20% de las veces y nadie sabe por qué
  2. Subset incompleto: solo están los endpoints más evidentes, el agente no puede ejecutar tareas complejas que requieren orquestar varios endpoints
  3. Sin validación de output: el agente procesa respuestas de API que cambiaron de formato, genera outputs incorrectos, el bug llega al usuario final

El cuarto error es el más costoso: no actualizar las tools en CI. El backend regenera la spec y el repo del agente sigue con la versión de hace tres meses. Con el patrón de package compartido, ese desajuste es imposible.

Si ya tienes la API, ya tienes el 80% del agente

No hace falta construir desde cero. Si tu empresa tiene una API REST documentada con OpenAPI, la capa de tools está a un script de distancia. Lo que queda —el ejecutor con validación, los filtros de seguridad, el agente orquestador— es el 20% donde más valor aporta la experiencia.

¿Tienes una API y quieres conectarla a un agente Claude? En 30 minutos te digo si es viable y cuánto tiempo llevaría:

Hablamos por WhatsApp →

Compartir artículo

LinkedInXWhatsApp

¿Procesos repetitivos en tu empresa?

Descarga gratis el Mapa de Automatización IA — los 5 procesos que más tiempo roban y cómo resolverlos.

Responsable: DailyMP (Daily Miranda Pardo). Usamos tu email solo para enviarte el PDF, salvo que marques la casilla de abajo. Más info en la política de privacidad.

Escrito por Daily Miranda Pardo

Ayudo a empresas a automatizar procesos, crear agentes IA y conectar sistemas inteligentes.