← SCRAM AI Lab

Herramientas IA

Bind ERP como MCP server: el caso SCRAM

Exponer Bind ERP via Model Context Protocol en vez de un wrapper API por cliente. Una capa, varios LLMs (Claude, Cursor, GPT) hablando con el mismo ERP. Rate limits y Redis incluido.

May 21, 2026

4 lecturas

El wrapper API por cliente es la deuda técnica del 2025

Cuando en SCRAM decidimos que Claude tenía que hablar directamente con Bind ERP — para que el equipo pudiera preguntar por clientes, facturas e inventario sin abrir tres pestañas — la respuesta fácil habría sido un backend NestJS con endpoints REST/GraphQL custom por consumidor. La respuesta correcta fue un MCP server: una capa que cualquier cliente AI (Claude Desktop, Cursor, ChatGPT con conector, agentes propios) consume con el mismo schema. Un servidor, múltiples consumidores, cero código por cliente nuevo. Hoy el mismo servidor sirve a tres equipos internos y a dos integraciones externas sin que tocáramos una línea entre uno y otro.

Por qué MCP gana sobre REST tradicional

  • Discovery automático: el cliente AI pregunta al server qué tools tiene; no necesitas documentar OpenAPI ni que el modelo lo "aprenda"
  • Schema tipado: cada tool declara inputs/outputs con JSON Schema y el LLM no alucina parámetros
  • Permisos granulares: el server decide qué tools expone según el token del cliente
  • Stateless del lado AI: el cliente AI no carga credenciales de Bind; solo habla con el MCP server con su token

Schema básico del MCP server

// scram-bind-mcp/src/tools.ts
export const TOOLS = [
  {
    name: 'bind_search_customers',
    description: 'Busca clientes en Bind ERP por nombre, RFC o email',
    inputSchema: {
      type: 'object',
      properties: {
        query: { type: 'string', minLength: 2 },
        limit: { type: 'number', default: 10, maximum: 50 },
      },
      required: ['query'],
    },
  },
  {
    name: 'bind_get_invoice_status',
    description: 'Obtiene el status de una factura por serie/folio',
    inputSchema: {
      type: 'object',
      properties: {
        serie: { type: 'string' },
        folio: { type: 'string' },
      },
      required: ['serie', 'folio'],
    },
  },
  {
    name: 'bind_get_inventory',
    description: 'Consulta inventario por SKU o almacén',
    inputSchema: {
      type: 'object',
      properties: {
        sku: { type: 'string' },
        almacen_id: { type: 'string' },
      },
    },
  },
];

El problema real: rate limits de Bind

Bind acepta ~30 requests por minuto antes de empezar a tirar 429. Un solo agente AI haciendo "dame los 50 clientes con factura vencida y sus últimos 3 pedidos" puede quemar el budget en segundos. Solución que probamos en producción: Redis como cache con TTL 5 minutos sobre todas las queries de lectura.

async function callBind(endpoint: string, params: any) {
  const cacheKey = `bind:${endpoint}:${hash(params)}`;
  const cached = await redis.get(cacheKey);
  if (cached) return JSON.parse(cached);

  await rateLimiter.acquire(); // token bucket 30/min
  const res = await fetch(`${BIND_API}/${endpoint}`, {
    headers: { Authorization: `Bearer ${BIND_TOKEN}` },
    body: JSON.stringify(params),
  });
  const data = await res.json();
  await redis.setex(cacheKey, 300, JSON.stringify(data));
  return data;
}

Para queries de escritura (crear pedido, actualizar inventario) no cacheamos pero sí pasamos por el rate limiter y agregamos un retry con backoff cuando llega 429.

Lo que NO se expone al agente

Listamos arriba lectura. Para escritura, el MCP solo expone tools idempotentes y de bajo blast radius:

  • Sí: crear nota de cliente, agendar tarea, actualizar teléfono
  • No: timbrar factura, cancelar pedido, ajustar inventario, mover dinero

La regla operativa que mantenemos sin excepciones: cualquier acción con consecuencia fiscal o financiera requiere confirmación humana en UI separada. El agente puede preparar la factura y mostrar el preview, pero el "timbrar" es un click humano. No es desconfianza al modelo; es compliance básico para SAT.

Stack en producción

Node 20 + @modelcontextprotocol/sdk, expuesto vía stdio para Claude Desktop y vía HTTP/SSE para clientes web. Corre en Docker sobre nuestro e2-standard-4 con soft limits (reservation 256MB, sin hard limits — la regla de la casa). Logs estructurados a Loki con label mcp_server=scram-bind y session_id por cliente AI. Métricas a Prometheus: latencia por tool, hits de cache, rate-limit acquisitions.

La pregunta para la siguiente iteración: ¿deberían las tools del MCP server ser generadas desde el catálogo de endpoints de Bind, o curadas a mano? Spoiler: a mano hasta que tengas más de 40 tools. Generación automática multiplica las herramientas que el modelo tiene que filtrar y degrada la precisión.

mcp
bind-erp
integraciones-mx
← Volver a SCRAM AI Lab