1. ¿Para qué entender tool-call?
Simplificando, una aplicación web convencional funciona según el esquema «el usuario pulsa un botón — nosotros llamamos a una función». En el mundo de las ChatGPT Apps el panorama es distinto: el usuario dice algo, el modelo piensa y, si lo considera oportuno, forma una invocación estructurada de una herramienta (tool-call).
Es decir, tú no escribes:
onClick={() => callSuggestGiftsApi(formData)}
sino que en su lugar:
- Describes la herramienta suggest_gifts (nombre, descripción y esquema de argumentos).
- Le explicas al modelo en el system-prompt para qué sirve esa herramienta.
- Le cedes el control al modelo: él decide cuándo y cómo invocarla.
De aquí se desprenden dos ideas clave que conviene entender desde el principio:
- GPT no ve tu código de backend. Solo ve la «cabecera» de la herramienta: su nombre, descripción y el esquema de parámetros.
- Lo «inteligentemente» que el modelo use tu App depende casi directamente de cómo redactes esas descripciones. Unas buenas descripciones son tu «prompt de herramienta».
Esta lección trata precisamente de ese «cerebro» entre el usuario y tu servidor.
2. Modelo mental de tool-call: qué ocurre en realidad
Empecemos con la visión completa. Un escenario típico para GiftGenius:
- Usuario: «Sugiere un regalo para un amigo de 30 años, presupuesto 100 dólares; le gustan los videojuegos».
- GPT lee ese mensaje y mira qué herramientas hay. En nuestra App, por ejemplo, está suggest_gifts.
- GPT decide: «Para responder bien, necesito invocar esta herramienta».
- En lugar de una respuesta de texto normal, genera una estructura: nombre de la herramienta + argumentos JSON.
- El cliente de ChatGPT ve: «Ajá, es un tool-call», y lo envía a tu MCP/servidor.
- Tu servidor ejecuta la lógica de negocio y devuelve un structured output.
- GPT recibe el resultado, lo lee y, basándose en la respuesta de la herramienta, forma una respuesta comprensible para el usuario y/o actualiza el widget.
Desde el punto de vista del OpenAI API, es el mismo mecanismo de LLM-function-calling: en la respuesta del modelo, en vez de texto normal, aparece un objeto con el name de la herramienta y los arguments, y finish_reason se marca como tool_calls. El modelo no ejecuta código por sí mismo: solo propone qué herramienta invocar; la invocación real la hace el cliente (ChatGPT/Apps SDK).
Se ve más o menos así (secuencia simplificada):
sequenceDiagram
participant U as Usuario
participant G as GPT (modelo)
participant C as Cliente de ChatGPT
participant S as Tu MCP/Backend
U->>G: "Sugiere un regalo para un amigo..."
G->>C: tool-call: { name: "suggest_gifts", args: {...} }
C->>S: HTTP /mcp tools/call (suggest_gifts, args)
S-->>C: Resultado (JSON con la lista de regalos)
C-->>G: tool result
G-->>U: Respuesta + widget actualizado
La conclusión principal: no escribes if (userAskedAboutGifts) callSuggestGifts(). Creas una herramienta y su descripción, y la decisión la toma el modelo.
3. Qué ve el modelo: System Prompt + lista de herramientas
Para entender cómo GPT decide qué hacer, hay que tener claro con qué información cuenta en el momento de elegir.
De forma simplificada, el modelo ve:
- el system‑prompt de tu App (lo veremos con detalle en el módulo 5);
- el historial del diálogo: mensajes del usuario, sus propias respuestas y resultados de tool-calls anteriores;
- la lista de herramientas disponibles (tools) con sus nombres, descripciones y esquemas de parámetros;
- anotaciones adicionales de las herramientas (readOnly/destructive, etc.).
Y no ve:
- la implementación de las funciones;
- consultas SQL;
- la estructura de tus tablas;
- el contenido del repositorio privado con el servicio.
Más adelante hablaremos en detalle de MCP. Por ahora basta con saber que, a nivel de MCP, las herramientas se declaran como descriptores: cada una tiene name, description e inputSchema (JSON Schema). En el handshake, ChatGPT solicita al servidor MCP la lista de herramientas y empieza a percibirlas como «acciones» disponibles.
Un ejemplo de descriptor para GiftGenius (JSON simplificado):
{
"name": "suggest_gifts",
"description": "Sugiere ideas de regalos según la edad, los intereses y el presupuesto",
"inputSchema": {
"type": "object",
"properties": {
"age": { "type": "integer" },
"budget": { "type": "number" }
},
"required": ["age", "budget"]
}
}
El modelo «lee» aquí solo el texto y la estructura: qué es age, qué es budget, qué hace la herramienta en general. La próxima lección tratará precisamente de cómo describir bien inputSchema. Ahora — de cómo a partir de esa descripción nace la decisión «voy a invocar suggest_gifts».
4. Cómo se ve un tool-call desde el API
ChatGPT invoca las herramientas (tools) de tu servidor MCP de forma muy similar a como un agente de OpenAI invoca funciones en tu backend. En el ChatGPT Apps SDK todo está un poco más envuelto, pero la mecánica básica es igual.
Imaginemos que, en nuestro backend, hacemos una petición normal al OpenAI API y pasamos la herramienta suggest_gifts, que el modelo puede invocar en su respuesta:
const response = await openai.responses.create({
model: 'gpt-5-mini',
messages: [
{
role: 'user',
content: 'Necesito un regalo para un amigo de 30 años, presupuesto 100 dólares'
}
],
tools: [ // aquí pasamos la lista de funciones que la LLM puede "invocar"
{
name: 'suggest_gifts',
description: 'Sugiere regalos por edad, presupuesto e intereses',
parameters: {
type: 'object',
properties: {
age: { type: 'integer' },
budget: { type: 'number' }
},
required: ['age', 'budget']
}
}
]
});
Si el modelo decide invocar la herramienta, recibirás como respuesta no texto, sino un mensaje del asistente con algo como:
{
"role": "assistant",
"tool_calls": [
{
"id": "call_1",
"name": "suggest_gifts",
"arguments": "{\"age\":30,\"budget\":100}"
}
],
"content": []
}
De este modo la LLM le dice a tu backend que necesita llamar a la función suggest_gifts(30,100).
Aquí hay tres cosas importantes:
- El nombre de la herramienta (name): el modelo realmente pone aquí la cadena que indicaste en la descripción de tools al enviar la primera petición.
- Los argumentos (arguments): una cadena JSON construida a partir de parameters/inputSchema.
- La ausencia de respuesta de texto normal (por ahora): en su lugar recibes la estructura para invocar la herramienta.
En el funcionamiento de las aplicaciones de ChatGPT ocurre lo mismo: el modelo devuelve «quiero invocar suggest_gifts con estos parámetros», y el cliente (ChatGPT) hace una petición HTTP a tu MCP/servidor: tools/call con el nombre de la herramienta y los argumentos.
5. Cómo decide exactamente el modelo: herramienta o texto
Ahora lo más interesante: ¿cuándo recuerda GPT tus herramientas?
La mecánica, simplificada, es así:
- El modelo ve el mensaje del usuario y el contexto actual.
- Internamente tiene una «capa» que genera el siguiente mensaje del asistente, pero en lugar de emitir siempre texto normal, el modelo puede elegir una de las formas de finalización:
- respuesta de texto normal (finish_reason: "stop");
- una o varias tool-call (finish_reason: "tool_calls");
- a veces otras opciones (por ejemplo, «hace falta otro mensaje del usuario»).
- En esta elección influyen:
- cuánto se parece la petición del usuario a las tareas descritas en tus herramientas;
- lo explícitamente que tu descripción diga «úsame justo en este caso»;
- los datos del app system prompt, que en el Apps SDK se configuran en la aplicación.
En términos humanos, el modelo «encaja» tu herramienta con la petición actual. Si la descripción dice: «Sugiere regalos por edad e intereses», y el usuario pide «análisis del presupuesto del Estado», el modelo ni intentará invocarla. Si la descripción es demasiado vaga — «hace cosas guays» — el modelo no entenderá para qué peticiones debe usarla.
Un matiz interesante: el modelo no está obligado a invocar la herramienta, aunque la hayas descrito. GPT puede decidir: «Aquí está todo claro, responderé yo, sin tool‑call». Por eso, más adelante en el curso, vamos a practicar activamente escribir descripciones de herramientas que hagan su uso lo más obvio y ventajoso posible para el modelo.
6. Nombre de la herramienta: por qué tool1 es una mala idea
El nombre de la herramienta es, en esencia, el identificador que el modelo usará en sus invocaciones. Podría parecer un campo puramente técnico, pero en la práctica el nombre influye mucho en el comportamiento del modelo.
Si llamas a la herramienta tool1, el modelo no entenderá nada. Para él es solo un conjunto de símbolos. Si la llamas suggest_gifts, search_products o fetch_user_orders, el propio nombre ya da una señal fuerte de qué trata esa herramienta.
Piensa en cómo lees tú código desconocido. Al ver una función calculateCartTotal, te haces una idea aproximada de qué esperar. El modelo necesita el mismo «ancla semántica».
Para GiftGenius, nombres lógicos pueden ser:
suggest_gifts
search_products
get_product_details
create_order
Es bueno si el nombre:
- es corto pero significativo;
- sigue un estilo uniforme (snake_case, latín, verbo_sustantivo);
- refleja una acción concreta.
Es mala idea mezclar acciones distintas en una sola herramienta, como do_all_gift_stuff. Al modelo le cuesta más entender cuándo usarla y, en las siguientes lecciones, veremos cómo esto rompe el esquema de argumentos y complica la depuración.
7. Descripción de la herramienta: tu prompt para el modelo
Si el nombre es el título, description es una mini documentación, pero no para la persona desarrolladora, sino para GPT. La persona leerá el código; el modelo, no. Se apoya en el texto de la descripción para decidir cuándo invocar la herramienta y qué argumentos poner.
Conviene escribir la descripción como «instrucciones de uso»:
- cuándo usar la herramienta;
- qué limitaciones tiene;
- qué no debe hacer.
Tomemos suggest_gifts. He aquí tres variantes de descripciones.
Demasiado amplia:
"Sugiere regalos."
El modelo no entenderá con respecto a qué, para quién, con qué parámetros. Esta herramienta puede «competir» en la cabeza del modelo con su conocimiento general sobre regalos, y a menudo decidirá responder solo con texto.
Demasiado estrecha:
"Sugiere regalos solo para hermanos pequeños en su cumpleaños."
Aquí, de hecho, hemos prohibido utilizar la herramienta casi siempre. Cualquier otro escenario — madre, colega, aniversario — «no encaja», y el modelo evitará invocarla.
Óptima:
"Usa esta herramienta cuando necesites sugerir regalos para una persona según su edad, tipo de relación (amigo, pareja, colega, etc.), presupuesto e intereses.
No la invoques para preguntas no relacionadas con regalos (por ejemplo, política o el tiempo)."
Aquí se detalla claramente qué hace la herramienta, qué parámetros tiene para ello y cuándo invocarla, además de añadir una condición negativa — para qué tipos de consultas no conviene usarla.
Al modelo le «gustan» estos límites claros. Cuanto más nítidamente indiques para qué formulaciones del usuario (intenciones) es pertinente la herramienta, más predecible será el comportamiento de la App.
Mini ejercicio
Puedes ahora mismo, sin levantarte del monitor, tomar tu futura App (puede no ser sobre regalos) e idear para una de sus herramientas tres descripciones: muy amplia, muy estrecha y equilibrada. Luego prueba cómo se comporta GPT con las distintas versiones.
8. Esquema de argumentos: cómo ayuda en la decisión
Hablaremos en detalle de JSON Schema en la próxima lección, pero para entender los tool-call conviene una intuición de alto nivel.
Cuando el modelo decide invocar una herramienta, necesita:
- Entender qué argumentos espera la herramienta.
- Extraer esos valores del texto del usuario (o del contexto).
- Formar un JSON con esos argumentos.
Para ello, en la descripción de la herramienta hay un esquema de parámetros (inputSchema) que le dice al modelo:
- qué campos hay (age, budget, relationship_type, interests, etc.);
- qué campos son obligatorios (required);
- qué tipos existen (integer, number, string, arrays, etc.);
- a veces — qué valores son válidos (enum) y aclaraciones de los campos (description).
Una interfaz TypeScript muy simple para los parámetros de suggest_gifts puede verse así:
interface SuggestGiftsParams {
age: number;
relationship_type: 'friend' | 'partner' | 'colleague';
budget: number;
interests?: string[];
}
A nivel de modelo, esto se convierte en un JSON Schema, y el modelo por el nombre y la descripción de cada campo deduce que:
- age se toma de frases como «30 años», «para un adolescente», etc.;
- budget de «presupuesto 100 dólares», «hasta 50 euros»;
- relationship_type de «amigo», «colega»;
- interests — de «le gustan los videojuegos».
Si proporcionas un esquema sin descripciones y con nombres de campos abstractos (a, b, c), el modelo fallará mucho más al rellenar los argumentos. Volveremos a esto en el módulo de localización y sugerencias de UX. La idea clave aquí es simple: el esquema no es solo validación en el backend; es ante todo una pista para el modelo sobre qué poner y dónde.
Ya hemos hablado de cómo el esquema ayuda al modelo a montar bien los argumentos. Pero además de «qué y cómo invocar», también importa «si se puede invocar ahora mismo y cuán seguro es». Aquí entran en juego los permisos y la metainformación de las herramientas.
9. Permisos y contexto: no todas las herramientas están disponibles siempre
Además del nombre, la descripción y el esquema de argumentos, hay otra dimensión esencial: la seguridad y el acceso. Las herramientas en una App real varían mucho en su nivel de «peligro». No es lo mismo buscar regalos en un catálogo público que cargar dinero a la tarjeta de un usuario.
El Apps SDK y MCP permiten reflejar esto en la descripción y las anotaciones de las herramientas — por ejemplo, marcarlas como read-only o destructive.
La idea es:
- Las herramientas que solo leen datos públicos (search_products, get_weather) se pueden invocar sin confirmaciones adicionales.
- Las herramientas que modifican algo (create_order, cancel_order, charge_user) se marcan como «destructivas». La UI de ChatGPT puede pedir confirmación adicional al usuario («¿Seguro que quieres realizar el pedido?»), y el propio modelo tenderá a proponerlas con menos frecuencia sin una petición explícita.
En módulos futuros, cuando configuremos MCP, verás cómo estas anotaciones (_meta, destructiveHint, readOnlyHint) aparecen en descriptores JSON reales, cómo influyen en la UX y en cómo ChatGPT forma diálogos tipo «Are you sure?» antes de invocar. Por ahora basta con entender:
- GPT tiene en cuenta no solo el texto de la descripción, sino también la metainformación de seguridad.
- Una herramienta que requiere autenticación no se usará hasta que el usuario inicie sesión (o hasta que la App obtenga el token necesario).
Este es otro factor que influye en la decisión de «invocar la herramienta o no»: incluso si por sentido la herramienta encaja, puede que no esté disponible por permisos, y entonces el modelo elegirá otra vía.
10. De dónde salen las herramientas en ChatGPT
Desde el punto de vista arquitectónico, una herramienta puede llegar al modelo por dos vías principales.
Primero, desde la configuración de tu ChatGPT App. Cuando registras la App, indicas qué servidores MCP (y sus herramientas) están vinculados, o qué tools incorporadas hay en la propia aplicación. Al iniciar la sesión, ChatGPT recibe esta configuración y entiende qué herramientas están disponibles.
Segundo, directamente desde MCP. MCP (Model Context Protocol) define una forma estándar para que el cliente (en nuestro caso ChatGPT/Apps SDK) sepa qué puede hacer tu servidor: realiza una petición tools/list, recibe un JSON con las descripciones de las herramientas y las guarda como capabilities. Veremos la mecánica detallada en un módulo aparte sobre MCP; ahora basta con captar la idea general.
Esquemáticamente:
flowchart LR A[ChatGPT Client] -->|handshake| B[MCP Server] B -->|tools/list| A A -->|envía la lista| G[GPT Model]
Después de esto, la lista de herramientas pasa a formar parte del contexto del modelo. Si cambias el esquema o la descripción de la herramienta en el servidor y reinicias la App, el nuevo descriptor llegará a ChatGPT en el siguiente handshake y el modelo empezará a tomar decisiones distintas sobre la invocación.
Y una idea práctica importante: cuando solo tocas el backend (la implementación de la herramienta), el modelo no lo sabe. Pero cuando cambias name/description/schema, cambias realmente el «cerebro» de la App. A veces es más útil ajustar una línea en description que escribir 200 líneas de código con heurísticas.
11. Apliquémoslo a GiftGenius: creando una herramienta que el modelo quiera invocar
Ahora conectemos todo cuidadosamente con nuestra aplicación de ejemplo GiftGenius. Imaginemos que ya tenemos un servidor MCP o una capa de backend en la que registramos herramientas. Registremos la herramienta suggest_gifts con server.registerTool(...).
Boceto primitivo en TypeScript (por ahora sin lógica real):
// pseudo-mcp-server/tools/suggestGifts.ts
server.registerTool(
'suggest_gifts', // nombre de la herramienta
{
title: 'Selección de regalos',
description:
'Usa esta herramienta para sugerir ideas de regalos según la edad, ' +
'el tipo de relación y el presupuesto. No la invoques para preguntas no relacionadas con regalos.',
inputSchema: { // descripción de los parámetros de la herramienta
type: 'object',
properties: {
age: { type: 'integer', description: 'Edad del destinatario en años' },
relationship_type: {
type: 'string',
description: 'Tipo de relación: friend, partner, colleague'
},
budget: {
type: 'number',
description: 'Presupuesto máximo para el regalo en la moneda del usuario'
}
},
required: ['age', 'budget']
}
},
async ({ age, relationship_type, budget }) => { // código de la función/herramienta
// La lógica real vendrá después
return { suggestions: [] };
}
);
Fíjate en los detalles que ya hemos cuidado en esta fase, aunque la lógica sea aún un «stub»:
- Nombre: suggest_gifts, y no tool1.
- Descripción: explica explícitamente cuándo invocar la herramienta y cuándo no.
- Descripciones de campos: ayudan al modelo a mapear correctamente el texto del usuario a los argumentos.
Como resultado, cuando el usuario escriba «Elige un regalo para un colega por 50 dólares», el modelo verá que:
- hay una herramienta llamada suggest_gifts con una descripción sobre sugerir regalos;
- tiene los campos age, relationship_type, budget;
- budget es «presupuesto máximo para el regalo», relationship_type es «tipo de relación: friend, partner, colleague».
Aunque los usuarios se expresen de forma imprecisa («hasta cincuenta», «para mi compañero de proyecto»), el modelo tendrá suficiente contexto para intentar construir el JSON de argumentos correctamente.
Cuando nuestra herramienta funcione de verdad (en el módulo sobre backend y MCP), ya estarás muy orientado en el tema: GPT la invocará de forma predecible simplemente porque hemos diseñado bien el interfaz y la descripción.
12. Un poco de práctica para ti
Para que el tema no se quede en teoría, te recomiendo hacer un pequeño experimento justo después de la lección.
Primero, toma uno de tus escenarios de GiftGenius o inventa una nueva App. Anota en papel o en un editor una función que quieras exponer al modelo — algo como search_products, find_hotels, calculate_shipping.
Luego inventa para esa misma herramienta tres pares «nombre + descripción»:
- Nombre y descripción muy abstractos.
- Demasiado concretos (casi un caso especial).
- Nombre + descripción bien equilibrados, donde se diga claramente cuándo invocar la herramienta y qué no debe hacer.
Después — si quieres — puedes, usando el SDK normal de OpenAI, hacer una petición sencilla con estas variantes y ver cómo cambia el comportamiento del modelo: si se invoca la herramienta, cómo rellena los argumentos. En la investigación sobre este tema se propone justamente un ejercicio de este tipo para suggest_gifts.
13. Errores típicos al diseñar tool-call y descripciones
Error n.º 1: Llamar a las herramientas tool1, handler, doStuff.
Ese naming es absolutamente inútil para el modelo. GPT no adivina las «intenciones del desarrollador» por el nombre del archivo; necesita un nombre semánticamente claro. Si le das un conjunto de tool1, tool2, tool3 sin descripciones, la herramienta prácticamente no se invocará: el modelo sencillamente no entenderá qué hace cada una y o bien las ignorará, o elegirá al azar.
Error n.º 2: Tratar description como comentarios para personas.
Muchos escriben algo formal como «Función para sugerir regalos», pensando que los detalles ya se saben por el código. Pero el modelo no ve el código; solo ve el texto de la descripción y el esquema de argumentos. Una descripción imprecisa se convierte en una fuente de alucinaciones: GPT o intentará responder por su cuenta cuando debería invocar la herramienta, o la invocará en situaciones extrañas.
Error n.º 3: Hacer la descripción demasiado amplia o demasiado estrecha.
Si escribes «Hace cosas guays», el modelo no entiende los límites de aplicación. Si escribes «Sugiere un regalo solo para el hermano pequeño en su 18.º cumpleaños», prácticamente prohíbes usar la herramienta casi siempre. La descripción óptima delimita claramente el ámbito de tareas (sugerir regalos según varios parámetros), la lista de parámetros clave (edad, relación, presupuesto, intereses) y especifica para qué clases de preguntas no debe usarse.
Error n.º 4: Ignorar el esquema de argumentos como parte del «prompt».
Algunos desarrolladores perciben JSON Schema solo como un medio de validación en el servidor. En realidad, el modelo analiza activamente los nombres de los campos, sus tipos y descripciones para entender qué datos extraer del texto del usuario. Si llamas a un campo x sin descripción y lo haces opcional, GPT empezará a rellenarlo de forma caótica o no lo rellenará. Un esquema correcto con nombres claros y descripciones breves reduce mucho la cantidad de tool-call no válidos.
Error n.º 5: Esperar que el modelo «tenga que» invocar la herramienta.
A veces los desarrolladores se sorprenden: «¿Por qué GPT no invocó mi herramienta, si existe?». La respuesta casi siempre es la misma: de la descripción o del system prompt no se desprende que la herramienta sea necesaria para esa pregunta, o el texto de la petición cae en una zona donde el modelo cree que le resulta más fácil responder por sí mismo.
Error n.º 6: Mezclar varias acciones heterogéneas en una sola herramienta.
A veces apetece hacer un manage_orders universal que busque pedidos, cree nuevos y cancele antiguos. Para una persona se puede explicar, pero para el modelo resulta una herramienta difusa y sin límites claros. GPT entiende peor cuándo invocarla y le cuesta más rellenar los argumentos — porque dentro aparecen un montón de campos opcionales. Es mejor separar esas acciones en varias herramientas más estrechas (get_order, create_order, cancel_order) con descripciones y esquemas claros.
Error n.º 7: No tener en cuenta permisos y seguridad en el diseño de la herramienta.
Si describes una herramienta que puede realizar acciones destructivas (cargos, borrado de datos), pero no la marcas como destructive y no limitas su ámbito de uso en la descripción, creas un riesgo. La UI de ChatGPT no pedirá confirmaciones adicionales y el modelo podría decidir invocarla incluso en escenarios «frontera». Las anotaciones correctas y una descripción cuidadosa («úsala solo tras el consentimiento explícito del usuario») ayudan a reducir esos riesgos ya al nivel del tool-call.
GO TO FULL VERSION