Playbook
No le entregues las llaves al servidor MCP.
El modelo reembolsó al cliente equivocado. Qué significa eso, por qué un chat con tools no es un plugin, y en qué orden cierras la caja.
Actualizado Por LUINIngeniería de IA · Seguridad
Finanzas vio el reembolso primero, un jueves. Soporte vio el ticket después. Ingeniería vio una ventana de chat que decía “claro, ya lo reembolsé”.
Un cliente le pidió ayuda al asistente. El asistente no es una persona. Es un modelo con permiso de hacer cosas: buscar un pedido, mandar un correo, mover dinero. En este producto esos permisos son tools en un servidor chico. En la industria eso se llama MCP — Model Context Protocol. Olvida la sigla un momento. Armaste una recepción que también puede abrir la caja.
La recepción reembolsó al cliente equivocado. No porque fuera malvada. Porque quiere ayudar, le pasaron un número de pago desde la conversación, y nadie dijo “solo reembolsas un pago que sea de quien está hablando”. La etiquetita de la tool decía “solo el usuario actual”. Las etiquetas no son candados.
Esa es toda la historia, y está pasando en productos que en el demo se ven bien. Una laptop. Una pregunta amable. Un check verde. Sin una segunda empresa en el edificio. Sin alguien tecleando el número de otro cliente nada más para ver qué pasa. El modelo sí lo va a hacer. Está tratando de terminar la frase.
Lo que en realidad publicaste
No publicaste un chatbot. Publicaste una puerta a los sistemas que guardan dinero, archivos y datos de clientes. El modelo se para en esa puerta y toca tan rápido como piensa. Si la puerta se abre para cualquiera con un gafete, y el gafete no dice de quién es el cajón, el reembolso del segundo cliente no es un hack. Es el default.
Dos preguntas distintas se aplastan en una:
¿Quién está en la puerta? Un usuario con sesión, un token. El gafete.
¿Qué puede tocar? Este pago, esta empresa, este archivo. La llave de un cajón.
La mayoría instala el gafete y se detiene. La llave se queda en el gancho porque el demo nunca pidió otro cajón.
Si lees código, esta es la puerta sin segunda pregunta. Si no lees código, lee las dos líneas en español: la etiqueta promete “usuario actual”. La siguiente línea reembolsa el número que haya llegado.
server.registerTool(
"refund_payment",
{
description: "Reembolsa un pago del usuario actual",
inputSchema: z.object({ paymentId: z.string() }),
},
async ({ paymentId }, extra) => {
if (!extra.authInfo) {
throw new Error("Unauthorized");
}
// El modelo mandó el paymentId. Nadie preguntó de quién era.
await payments.refund(paymentId);
return { content: [{ type: "text", text: "refunded" }] };
},
);La description es un deseo. La función es la empresa.
El gafete que se fue a la bodega
El mismo gafete que abrió la puerta de recepción se fotocopió y se mandó al sistema de pagos. A payments nunca le dijeron “este gafete solo vale en recepción”. Entonces un gafete de recepción robado ya es la caja robada.
En ingeniería a eso le dicen token passthrough. La regla es la de un hotel: la tarjeta que abre el 412 no abre la rampa de carga. Recepción se queda con su tarjeta. Payments recibe otra, de corta vida, nombrada para payments.
La caja de herramientas con un soplete adentro
Junto a “reembolsa este pago” el equipo dejó “corre cualquier comando de la base”, porque el demo lo usó para asomarse a una fila. Eso no es una tool. Eso es darle al modelo las llaves de repuesto del edificio y pedirle que tenga cuidado.
La misma familia: leer cualquier archivo, escribirle a cualquiera, reembolsar sin tope y sin un folio que puedas buscar dos veces. Si no pondrías en el sitio un botón rojo que haga lo mismo, no se lo pongas al asistente.
Las fallas calladas son cómo el jueves pasa dos veces. El servidor que solo corría en una laptop se copia a una máquina que ve toda la oficina. Nadie anota quién pidió el reembolso, entonces el único record es un chat. La versión del servidor cambia cuando alguien le da a actualizar. El gafete no expira porque expirar rompía el demo.
No te va a llegar como “se nos olvidó la autorización”. Te va a llegar como un cliente preguntando por qué se movió su dinero.
Cierra la caja. No reconstruyas el hotel.
El instinto es un rewrite. Otro vendor, otro repo, otra sensación de orden. El rewrite esconde las tools que no debiste publicar. También esconde la única tool que ya mueve dinero. Quédate con la recepción. Cambia lo que tiene permitido tocar.
Hazlo en este orden. Cada línea responde algo que la siguiente necesita.
Anota cada botón que el asistente puede oprimir
Reembolsar, exportar, correo, “nada más un query”. Qué datos hay detrás de cada uno. Quién puede hablarle a este servidor. Si no puedes nombrar los botones, no tienes un producto. Tienes un demo que todavía carga credenciales de producción.
Quita la mayoría
Reembolsar un pago de este cliente: quédate. Correr cualquier comando contra la base: bórralo. La lista no es una feature. Es cada cuarto al que el modelo puede entrar sin preguntarte.
Checa el gafete, luego checa el cajón
El pase tiene que ser para este servidor, no para “algún pase”. Luego el pago tiene que ser de la persona del pase. Eso va en la función que mueve el dinero, no en la etiqueta. Una etiqueta bonita sobre un cajón abierto sigue siendo un cajón abierto.
Deja de fotocopiar el pase
La tarjeta de recepción se queda en recepción. Payments recibe la suya. Si la bodega acepta la tarjeta de la entrada, una fuga en el escritorio es una fuga en la bodega.
Escribe la línea que habría explicado el jueves
Quién pidió el reembolso. Qué pago. Si era suyo. Un número que puedas buscar. No un transcript de chat. Si no puedes ver que oprimen el botón, el viernes lo vas a gastar reescribiendo la etiqueta.
Dale a alguien cómo apagarlo
Un switch que apague reembolsos sin armar de nuevo el producto. Una versión que tú elegiste, no la que se actualizó sola en la madrugada. Un deploy que se pueda deshacer. “Diles a todos que reinicien” no es un switch.
Entonces sí, el siguiente botón
Exportar facturas, los mismos checks, la misma línea en los logs, el mismo switch. Agregar botones antes de cerrar la caja es cómo el path de reembolso se vuelve más servicial.
Trata cada botón nuevo como un mostrador que instaló un contratista mientras no estabas: lee qué hace, oprime el que puede perder dinero y rechaza cualquier cosa cuya historia de seguridad sea “se supone que el asistente solo lo usa para el usuario actual”.
Con qué te quedas
La velocidad era el punto. Un asistente que reembolsa al cliente correcto un martes es el producto. Un asistente que puede reembolsar a un cliente es un hoyo.
Listo no es “el chat terminó la tarea”. Listo es: a quien no debe se le niega, el pase no viaja, el jueves se ve sin preguntar en Slack, la cosa se puede apagar, y la prueba que habría cachado este reembolso existe antes del botón del viernes. El modelo puede seguir eligiendo la tool. Una persona sigue decidiendo si esa tool puede existir.
El equipo que hace esto bien no se ve más lento a las cuatro. Se ve tranquilo. Alguien agrega un botón. Alguien lo lee como una página pública del sitio. Los pases se cortan para una puerta cada uno. La propiedad se checa donde se mueve el dinero. Cuando el modelo se inventa un número de pago, la función ya decidió.
Siguen publicando un martes. Finanzas no se entera primero.
El botón de reembolso puede quedarse. Ya nada más no le cree al chat.
Práctica relacionada
¿Te sirvió?