1. Qué es el tool gating y por qué merece una lección aparte
Hasta ahora, en ejemplos simplificados hacíamos lo siguiente: describimos el conjunto de herramientas del App, conectamos el servidor MCP — y todo está disponible para el modelo siempre. Desde la perspectiva de «hacer una demo en 5 minutos», funciona. Desde la perspectiva de un producto real — no tanto.
Tool gating es un patrón en el que la lista de herramientas disponibles para el modelo no es fija, sino que depende del contexto: el paso del workflow, los permisos del usuario, el estado de los datos, etc.
Lo más importante: la lista de tools no es «un vertedero de todo lo que hayas escrito alguna vez», sino parte del diseño del escenario. Cuando diseñas el workflow, en realidad también diseñas qué herramientas tiene derecho a ver el modelo en cada etapa.
Una analogía sencilla: no le das a un becario en un banco acceso a todos los sistemas de golpe — primero solo lectura, luego operaciones simples y después cosas más serias. Aquí la lógica es la misma, solo que el becario es la LLM.
2. El problema de «todas las herramientas a la vez»: contaminación del contexto y seguridad
Si le das al modelo decenas de herramientas, empieza a sufrir en varios frentes: sobrecarga de contexto, confusión al elegir y cuestiones de seguridad. Estudios de OpenAI/Anthropic muestran que cuantas más funciones describes en el contexto, peor acierta el modelo con la adecuada.
En primer lugar, cada definición de herramienta son tokens: nombre, descripción, JSON Schema. Una lista de 30–40 tools se come fácilmente un par de miles de tokens. Son esos tokens que podrías dedicar al historial del diálogo, al contexto del usuario o a ejemplos de buenas respuestas. En su lugar, el modelo lee una «novela» sobre tus APIs.
En segundo lugar, cuando las herramientas se parecen, el modelo empieza a confundirse. Si tienes search_products y get_product_details, puede intentar llamar a get_product_details directamente con una consulta de texto, porque la descripción le pareció más adecuada.
Además surge la cuestión de seguridad. Hay un principio aburrido pero importante de mínimos privilegios (least privilege): el sistema solo debe tener las capacidades que realmente necesita «aquí y ahora». Si en la fase de introducción el modelo ya conoce checkout, basta una pequeña inyección de prompt por parte del usuario para que intente lanzar el pago antes de tiempo. El tool gating es un caso práctico cómodo de minimización de privilegios: en cada paso activamos solo lo necesario.
Y por último, UX. Si el modelo hace de repente algo «mágico» que el usuario no esperaba (por ejemplo, crea un pedido mientras la persona aún está eligiendo un regalo), la confianza en tu App cae en picado.
3. GiftGenius como ilustración de tool gating
Tomemos nuestro caso GiftGenius y veamos honestamente los pasos:
- Entrevista: averiguamos la edad, el sexo, los intereses del destinatario, el presupuesto, etc.
- Exploración: buscamos productos en el catálogo y mostramos ideas.
- Checkout: cuando el usuario ya ha elegido el regalo, pasamos a tramitarlo.
Si en el paso de la entrevista el modelo ya conoce search_products, add_to_cart y checkout, puede:
- empezar a invocar la búsqueda demasiado pronto, antes de haber reunido preferencias razonables;
- intentar «tramitar el pedido» de inmediato porque el usuario comentó «Oh, esto está bien, me lo quedo».
La opción correcta es cambiar la lista de tools disponibles a medida que se avanza por los pasos. A continuación analizaremos precisamente un escenario así: en el paso de entrevista solo se ven herramientas de guardado de preferencias, en el paso de exploración — búsqueda y añadir al carrito, y en el paso de checkout — propiamente checkout.
Resumámoslo en una tabla pequeña:
| Paso del workflow | Objetivo del paso | Qué herramientas están disponibles para el modelo | Qué «no ve» el modelo en este paso |
|---|---|---|---|
|
Recopilar el perfil del destinatario | |
|
|
Buscar y refinar ideas | |
+ (si el carrito está vacío) |
|
Finalizar las compras | |
Cualesquiera herramientas de «configuración» que ya no sean necesarias |
Fíjate: la herramienta checkout aparece solo cuando hay algo que tramitar y únicamente en el paso correspondiente. Este es el ejemplo clásico de tool gating para un escenario de comercio.
4. Estrategias de tool gating: por estado, por roles, por recursos
La variante más habitual es el state‑based gating (gating por pasos del workflow): la lista de herramientas depende del estado del escenario. Es decir, mantienes una variable step en algún lugar y, según ella, determinas qué herramientas se activan y cuáles no.
Pero no solo los pasos pueden influir en las herramientas.
A veces aplicas role‑based gating (por roles del usuario): el administrador tiene acceso a herramientas de servicio (por ejemplo, reindexar el catálogo); el usuario normal — solo a las de usuario. En ocasiones — resource‑based gating (por estado de recursos): la herramienta «abrir puerta» aparece solo si, en el estado del recurso, la puerta está marcada como cerrada.
Para no hablar en abstracto, describámoslo con una pequeña función en TypeScript. Imaginemos que tenemos un contexto con el paso, el rol, el carrito actual y el estado de algún recurso:
type WorkflowStep = 'interview' | 'browsing' | 'checkout';
type UserRole = 'user' | 'admin';
interface WorkflowContext {
step: WorkflowStep;
role: UserRole;
cartItems: number; // cuántos artículos hay en el carrito
doorIsClosed: boolean; // ejemplo de resource-based gating: estado de un recurso concreto
}
Ahora enumeremos qué tools existen en el sistema y cómo filtrarlas:
type ToolName =
| 'save_preference'
| 'finish_interview'
| 'search_products'
| 'get_product_details'
| 'add_to_cart'
| 'checkout'
| 'reindex_catalog'
| 'open_door';
const baseTools: ToolName[] = [
'save_preference',
'finish_interview',
'search_products',
'get_product_details',
'add_to_cart',
'checkout',
'reindex_catalog',
'open_door',
];
Aquí open_door es un ejemplo de herramienta que depende del estado de un recurso concreto (si la puerta está cerrada o no).
Y la propia función de gating:
function getAvailableTools(ctx: WorkflowContext): ToolName[] {
const byStep: ToolName[] =
ctx.step === 'interview'
? ['save_preference', 'finish_interview']
: ctx.step === 'browsing'
? ['search_products', 'get_product_details', 'add_to_cart']
: ['search_products', 'get_product_details', 'add_to_cart', 'checkout'];
const checkoutAllowed =
ctx.step === 'checkout' && ctx.cartItems > 0
? byStep
: byStep.filter((t) => t !== 'checkout');
const withAdmin =
ctx.role === 'admin'
? [...checkoutAllowed, 'reindex_catalog']
: checkoutAllowed;
const withResources =
ctx.doorIsClosed
? [...withAdmin, 'open_door']
: withAdmin.filter((t) => t !== 'open_door');
return withResources;
}
Aquí se ven bien tres «capas» de gating:
- por paso (byStep);
- por rol de usuario (withAdmin);
- por estado del recurso (withResources y el flag doorIsClosed).
No es código de un SDK, sino un simple boceto arquitectónico. Pero así es como suele pensarse sobre el tool gating: existe un catálogo completo de herramientas y una función que, según el contexto, devuelve un subconjunto.
5. Dónde vive el tool gating en la arquitectura del App
Relacionémoslo un poco con lo que ya sabes del stack de ChatGPT App.
En teoría, el protocolo MCP funciona así
En MCP, las herramientas no tienen por qué estar fijadas en un JSON estático: el servidor puede devolver la lista de forma dinámica en función de la sesión. Es más, en la especificación hay un mecanismo de capabilities, donde el servidor declara que su lista de herramientas puede cambiar, y notificaciones tools/list_changed para que el cliente (ChatGPT/agente) vuelva a solicitar la lista de tools cuando algo cambie.
Y formalmente puedes hacerlo así, y algunos clientes MCP funcionarán con una lista de herramientas MCP dinámica. Pero a día de hoy, ChatGPT App no soporta tools/list_changed. Puede que en el futuro esto cambie, pero por ahora ese enfoque no funcionará.
Lo que sí funciona es esto
Mantienes el estado y la lista de métodos disponibles del lado del modelo. Puedes enviar al modelo el state y la lista de tools disponibles en cada paso como parte de la «visión del mundo»: en el prompt del sistema describes explícitamente el paso actual (por ejemplo, step = "browsing"), flags clave (por ejemplo, cartItems = 2, role = "user") y adjuntas solo el subconjunto de herramientas que está permitido ahora.
El modelo no «sabe olvidar» herramientas por sí mismo, pero sigue muy bien las instrucciones explícitas del tipo: «En este paso solo puedes usar estas funciones…». Al final, toda la lógica de gating para el modelo se ve como un contrato sencillo: este es el estado actual del escenario, este es el listado de botones que puedes usar; lo demás «no existe» para ti. No requiere ninguna «magia» especial — basta con actualizar de forma consistente el state y la lista de tools en las peticiones al modelo cuando se transiciona entre pasos.
Además, puedes añadir instrucciones en structuredContent, algo así:
{
"instructions": {
"current_step": "browsing",
"enabled_mcp_tools": ["search", "apply"]
}
}
También puedes añadir protección a nivel de tu código de negocio. Incluso si la lista de tools ya está «actualizada», es importante duplicar la lógica de gating en los propios handlers, porque:
- el modelo puede olvidar instrucciones y/o datos si hubo una conversación larga;
- el modelo puede intentar invocar una herramienta «fantasma» que estuvo disponible en el paso anterior;
Por eso, un buen diseño: «ocultamos» las herramientas al modelo y, dentro del handler, comprobamos si ahora se puede hacer la acción.
6. Tool gating a nivel de modelo vs. lógico
Si lo conectamos con la sección anterior: todo lo que ocurre a nivel de llamada al modelo (qué flags/step pones en el prompt) es gating a nivel de modelo, y las comprobaciones en los propios handlers de herramientas — lógico.
Normalmente tiene sentido separar dos capas:
- Gating a nivel de modelo — cuando el modelo sabe que la herramienta «está permitida» justo ahora, porque simplemente le escribes explícitamente en las instrucciones qué funciones están disponibles en este paso. Para el modelo, el mundo se ve así: «este es el state actual, este es el conjunto de botones, no hay otros».
- Gating lógico — comprobaciones dentro de la propia herramienta. Incluso si el modelo intenta llamar a checkout antes de tiempo (por caché, memoria fantasma o porque en uno de los pasos anteriores le pusiste esa herramienta), el handler mira el estado actual y rechaza amablemente: algo como «primero elige un regalo y luego tramitamos el pedido» (¡y no simplemente lanza una excepción!).
¿Por qué hacen falta ambas capas? Porque la infraestructura alrededor de las LLM y los propios escenarios pueden comportarse de manera imperfecta:
- el modelo puede recordar que alguna vez vio la herramienta checkout e intentar referirse a ella en su razonamiento o incluso en un tool-call;
- tú mismo puedes, por error, pasar en algún paso un conjunto de tools más amplio de lo necesario, y el modelo empezará a usar funciones de más;
- clientes/capas intermedias pueden cachear la configuración de la llamada al modelo y durante un tiempo enviar el conjunto antiguo de herramientas.
En la práctica, esto implica una idea sencilla: es peligroso confiar solo en «no pusimos la herramienta en tools — listo, ya no se llamará nunca más». Las comprobaciones en los handlers siguen siendo necesarias.
Ejemplo de gating lógico en el handler de checkout en pseudo‑TypeScript:
async function checkoutTool(args: { paymentMethodId: string }, ctx: WorkflowContext) {
if (ctx.step !== 'checkout') {
return {
error: 'Checkout not available yet. Please finish selecting a gift first.',
};
}
if (ctx.cartItems === 0) {
return {
error: 'Your cart is empty. Add at least one gift before checkout.',
};
}
// ... lógica real de finalización
}
Esta respuesta ayuda tanto al usuario como al modelo: el modelo ve un error estructurado y puede corregir su plan de acción.
7. Cómo enlazar el tool gating con el UI y el widget
El tool gating no es solo cosa del servidor. El UI/UX también debe percibir los cambios.
El widget conoce el paso actual (ya hablamos de widgetState y de que ese estado puede guardar, por ejemplo, currentStep). El modelo también, porque el paso se pasa explícitamente en las herramientas o está incluido en el prompt del sistema. Es importante que el UI y el conjunto de tools activos estén sincronizados.
Si el modelo considera que ahora el paso es «Exploración», pero el widget muestra la interfaz de «Entrevista», el usuario se pierde. Si al contrario — el UI ya dibuja el botón «Pagar», pero checkout aún no está disponible, el modelo quedará en una posición extraña: hay botón pero la función «no funciona».
Pequeño esquema del ciclo de vida del paso teniendo en cuenta el tool gating:
flowchart TD A[El usuario rellena la entrevista en el widget] --> B[El widget invoca la herramienta save_preference / finish_interview] B --> C[MCP / backend actualiza state.step] C --> D[El servidor cambia el conjunto de herramientas para la sesión] D --> E[El cliente de ChatGPT actualiza las herramientas disponibles para el modelo] E --> F[El modelo hace nuevas preguntas
y/o invoca nuevas herramientas] C --> G[El widget recibe el nuevo paso vía widgetState
y cambia el UI]
Para el usuario, esto se ve como un asistente de toda la vida: primero unas preguntas, luego una lista de regalos y después la confirmación final. Pero por debajo, se cambian a la vez el UI, la lista de herramientas y la instrucción para el modelo.
En un widget de Next.js, esto puede expresarse de forma muy simple. Supongamos que guardas step en el widgetState:
type Step = 'interview' | 'browsing' | 'checkout';
function GiftWizardWidget() {
const [widgetState, setWidgetState] = useWidgetState<{ step: Step }>({
step: 'interview',
});
if (widgetState.step === 'interview') {
return <InterviewScreen onDone={() => setWidgetState({ step: 'browsing' })} />;
}
if (widgetState.step === 'browsing') {
return <BrowsingScreen onCheckout={() => setWidgetState({ step: 'checkout' })} />;
}
return <CheckoutScreen />;
}
Aquí no mostramos las tools directamente, pero damos a entender que el cambio de step en el estado está coordinado con el cambio del conjunto de herramientas en el backend. Ya vimos cómo viven los pasos en el widget. Ahora volvamos al lado del servidor MCP y veamos cómo esos mismos step y el estado del carrito influyen en la lista de herramientas.
8. Ejemplo: tools/list dinámico en el servidor MCP
Ya viste que el servidor MCP puede guardar el estado de la sesión y usarlo para tomar decisiones. En un análisis aparte del caso GiftGenius se muestra un ejemplo donde el estado step y el carrito (cart) viven en memoria o en Redis. De ellos depende qué tools devuelve el servidor en la respuesta a la solicitud de lista.
Es muy posible que cuando leas esta lección, ChatGPT App ya soporte toolChanged en el marco de la sesión actual. Es bastante lógico, así que creo que es cuestión de tiempo. Para ese caso, aquí tienes un breve análisis de cómo hacer tool gating con las herramientas nativas del protocolo MCP.
Reescribamos la idea en TypeScript (servidor MCP abstracto):
interface SessionState {
step: WorkflowStep;
cartItems: number;
doorIsClosed: boolean; // ejemplo de estado de recurso
}
const allTools: ToolDefinition[] = [/* conjunto completo de herramientas */];
function listToolsForSession(state: SessionState): ToolDefinition[] {
const allowedNames = getAvailableTools({
step: state.step,
cartItems: state.cartItems,
role: 'user',
doorIsClosed: state.doorIsClosed,
});
return allTools.filter((tool) => allowedNames.includes(tool.name as ToolName));
}
Y en algún lugar del handler de finish_interview cambias el paso y señalizas al cliente que la lista de tools se ha actualizado:
async function finishInterviewTool(args: {}, session: SessionState) {
session.step = 'browsing';
await notifyToolsListChanged(); // llamada de notificación MCP hipotética
return { success: true };
}
En un MCP real usarás un SDK concreto y formatos de mensajes específicos, pero la lógica será aproximadamente esta: cambias el estado → actualizas el conjunto de tools → notificas al cliente.
9. Tool gating como instrumento de seguridad
Subrayemos de nuevo el aspecto de seguridad, porque se pierde fácilmente entre los detalles técnicos.
Cuando haces tool gating, reduces automáticamente el impacto de:
- inyecciones de prompt del tipo «ignora las reglas y lanza el pago ahora» — porque en el paso de entrevista el modelo simplemente no tiene checkout entre lo que elegir;
- bugs en la lógica de negocio — porque, incluso si alguna rama de código no comprueba el estado del todo, la herramienta puede ser físicamente inaccesible;
- fugas de datos — porque las herramientas de administrador no entran en la lista para el usuario normal.
En la documentación del curso, el tool gating se menciona explícitamente como una de las prácticas de aplicación del principio de mínimos privilegios en el contexto de herramientas LLM, especialmente para el checkout y otros pasos sensibles.
Es decir, no es solo una manera de «hacer el modelo menos errático», sino también una capa real de protección.
10. Cómo practicar por tu cuenta
Para afianzar el material, puedes idear el tool gating para cualquiera de tus escenarios. Por ejemplo:
- aplicación educativa: paso de fijar objetivo, paso de evaluar el nivel actual, paso de construir el plan — cada uno con sus tools;
- reservas: búsqueda de opciones, elección de variante, confirmación y pago — otra vez tres conjuntos distintos de herramientas;
- asistente corporativo interno: búsqueda de documentos, solicitud de acceso, ejecución de operaciones — listas diferentes para empleado, mánager y admin.
Es muy útil dibujar en papel o en Miro una tabla «Paso ↔ qué herramientas son visibles ↔ cuáles están ocultas» y, frente a cada paso, formular brevemente por qué necesita precisamente esas tools y por qué hay que ocultar las demás.
11. Errores típicos al trabajar con tool gating
Error n.º 1: «Volcar» todas las herramientas a la vez y confiar en el modelo.
A veces el desarrollador piensa: «El modelo es inteligente, ya sabrá qué llamar y cuándo». En la práctica, esto lleva a contaminar el contexto, a más tokens y a más tool-calls erróneos. Duele especialmente cuando el modelo invoca checkout u otra herramienta peligrosa solo porque está en la lista. El tool gating existe precisamente para que esto no ocurra.
Error n.º 2: Creer que ocultar la herramienta en la lista es suficiente.
Incluso si el servidor MCP ya no devuelve la herramienta en tools/list, el modelo puede «recordarla» del historial, y la infraestructura — cachear el conjunto antiguo de tools. Como resultado, llega una llamada a una herramienta fantasma. Si el handler no hace comprobaciones lógicas, puede ejecutar la acción «en el momento equivocado». Por eso, el gating debe existir tanto a nivel de la lista de tools como dentro de los handlers.
Error n.º 3: Falta de sincronía entre el UI y el conjunto de herramientas.
Sucede que el widget ya pasó al paso "checkout" y muestra un bonito botón «Pagar», pero en el lado MCP olvidaste incluir checkout en la lista de herramientas disponibles. El modelo no entiende por qué el botón existe pero la herramienta no está disponible, y empieza a generar respuestas extrañas. O al revés: el conjunto de tools ya cambió, el modelo está listo para proponer regalos, pero el widget sigue haciendo preguntas de la entrevista. Al diseñar el workflow es importante actualizar en sincronía tanto el estado del UI como la lista de herramientas.
Error n.º 4: Lógica de gating demasiado compleja.
A veces, inspirado por las posibilidades, el desarrollador empieza a construir casi un diagrama BPMN completo con decenas de estados y condiciones para todos los casos. Al final, ni él mismo entiende a la semana por qué cierta herramienta está disponible solo los jueves de años bisiestos. Para la mayoría de los Apps basta con una escalera simple de pasos y reglas claras: por paso, por rol de usuario y por unos pocos flags clave en el estado.
Error n.º 5: Fijar rígidamente el tool gating en el prompt sin soporte en el servidor.
A veces se intenta resolver todo con palabras en el prompt del sistema: «En este paso no uses la herramienta checkout» — y, a la vez, no se cambia la lista real de herramientas ni se añaden comprobaciones en el backend. A veces el modelo obedecerá, a veces no, y obtendrás un comportamiento inestable. Las instrucciones en el prompt son útiles, pero deben complementar, no sustituir, el gating técnico en la infraestructura.
Error n.º 6: Ignorar roles y permisos.
En aplicaciones con autenticación, a menudo se olvida que el tool gating debe tener en cuenta no solo el paso, sino también el rol. Como resultado, un usuario sin permisos de administrador igualmente ve (o, peor, puede invocar) herramientas destinadas a soporte o DevOps. En el módulo sobre autorización ya viste cómo llegan los permisos al contexto; aquí es importante no olvidar usar esa información al elegir el conjunto de tools.
Error n.º 7: Ausencia de monitorización de tool-calls erróneos.
Si te equivocaste en algún lugar con el gating, el síntoma característico son errores más frecuentes del tipo «Tool not available», «MethodNotFound» o tus propios errores lógicos como «Checkout is not available yet». Si no recoges estadísticas de estos eventos, puedes tardar mucho en notar que los usuarios chocan regularmente con muros invisibles. Un simple registro y contadores por tipos de errores ayudan mucho a detectar a tiempo problemas en el diseño del workflow y del gating.
GO TO FULL VERSION