CodeGym /Corsi /ChatGPT Apps /Gestione dinamica dell’elenco degli strumenti (tool gatin...

Gestione dinamica dell’elenco degli strumenti (tool gating)

ChatGPT Apps
Livello 11 , Lezione 1
Disponibile

1. Che cos’è il tool gating e perché è un argomento a parte

Finora, negli esempi semplificati, facevamo così: descriviamo un set di strumenti per l’App, colleghiamo il server MCP — e tutto è sempre disponibile al modello. Dal punto di vista del «fare una demo in 5 minuti» funziona. Dal punto di vista di un prodotto reale — non proprio.

Tool gating è un pattern in cui l’elenco degli strumenti disponibili al modello non è fisso, ma dipende dal contesto: lo step del workflow, i permessi dell’utente, lo stato dei dati, ecc.

La cosa più importante: l’elenco dei tools non è «un mucchio casuale di tutto ciò che avete mai scritto», bensì parte del design dello scenario. Quando progettate il workflow, in pratica progettate anche quali strumenti il modello è autorizzato a vedere a ciascuna fase.

Un’analogia semplice: non date a un tirocinante in banca l’accesso a tutti i sistemi subito — prima solo consultazione, poi operazioni semplici, poi più serie. Qui la logica è la stessa, solo che il tirocinante è la LLM.

2. Il problema del «tutti gli strumenti subito»: rumore di contesto e sicurezza

Se date al modello decine di strumenti, soffre su più fronti: sovraccarico del contesto, confusione nella scelta e problemi di sicurezza. Ricerche di OpenAI/Anthropic mostrano che più funzioni descrivete nel contesto, peggio il modello sceglie quella giusta.

Primo, ogni definizione di strumento sono token: nome, descrizione, JSON Schema. Un elenco di 30–40 tools consuma facilmente un paio di migliaia di token. Sono proprio i token che potreste spendere sulla storia del dialogo, sul contesto dell’utente, su esempi di buone risposte. Invece il modello legge un «romanzo» sulle vostre API.

Secondo, quando gli strumenti sono simili, il modello inizia a confondersi. Se avete search_products e get_product_details, può provare a chiamare get_product_details direttamente con una query testuale, perché la descrizione gli è sembrata più adatta.

Inoltre, c’è anche il tema della sicurezza. Esiste un principio noioso ma importante, quello dei privilegi minimi (least privilege): un sistema deve avere solo le capacità realmente necessarie «qui e ora». Se allo step di presentazione il modello già conosce checkout, basta una piccola iniezione di prompt da parte dell’utente perché provi ad avviare il pagamento in anticipo. Il tool gating è un modo pratico per minimizzare i privilegi: a ogni step abilitiamo solo il necessario.

E infine, UX. Se il modello fa improvvisamente qualcosa di «magico» che l’utente non si aspettava (per esempio, crea un ordine mentre la persona sta ancora scegliendo un regalo), la fiducia nella vostra App crolla.

3. GiftGenius come illustrazione del tool gating

Prendiamo il nostro caso GiftGenius e guardiamo onestamente gli step:

  1. Intervista: raccogliamo età, genere, interessi del destinatario, budget, ecc.
  2. Selezione: cerchiamo prodotti nel catalogo, mostriamo idee.
  3. Checkout: quando l’utente ha già scelto il regalo, passiamo all’acquisto.

Se allo step di intervista il modello conosce già search_products, add_to_cart e checkout, potrebbe:

  • iniziare a chiamare la ricerca troppo presto, prima di aver raccolto preferenze decenti;
  • provare a «finalizzare l’ordine» subito, perché l’utente ha detto «Oh, questo va bene, lo prendo».

La variante corretta è cambiare l’elenco dei tools disponibili man mano che si procede. Più sotto analizzeremo proprio questo scenario: allo step di intervista sono visibili solo gli strumenti per salvare le preferenze, allo step di selezione — ricerca e aggiunta al carrello, allo step di checkout — il checkout vero e proprio.

Ricapitoliamo in una piccola tabella:

Step del workflow Obiettivo dello step Strumenti disponibili per il modello Cosa il modello non «vede» in questo step
INTERVIEW
Raccogliere il profilo del destinatario
save_preference, finish_interview
search_products, add_to_cart, checkout
BROWSING
Selezionare e affinare le idee
search_products, get_product_details, add_to_cart
save_preference
+
checkout
(se il carrello è vuoto)
CHECKOUT
Concludere gli acquisti
search_products, get_product_details, add_to_cart, checkout
Qualsiasi tool di «configurazione» non più necessario

Nota: lo strumento checkout appare solo quando c’è qualcosa da acquistare e solo nello step corrispondente. Questo è un classico esempio di tool gating per uno scenario di commerce.

4. Strategie di tool gating: per stato, per ruoli, per risorse

La variante più diffusa è il gating basato sullo stato (state‑based, per gli step del workflow): l’elenco degli strumenti dipende dallo stato dello scenario. Cioè da qualche parte mantenete una variabile step e, in base a essa, decidete quali strumenti sono abilitati e quali no.

Ma non sono solo gli step a influenzare gli strumenti.

Talvolta fate role‑based gating (per ruolo dell’utente): all’amministratore sono disponibili strumenti di servizio (ad esempio, reindicizzare il catalogo), all’utente normale — solo quelli utente. A volte — resource‑based gating (per stato delle risorse): lo strumento «apri porta» appare solo se nello stato della risorsa la porta è segnata come chiusa.

Per non restare nel vago, descriviamolo come una piccola funzione in TypeScript. Immaginiamo di avere un certo contesto con step, ruolo, carrello corrente e stato di una risorsa:

type WorkflowStep = 'interview' | 'browsing' | 'checkout';
type UserRole = 'user' | 'admin';

interface WorkflowContext {
  step: WorkflowStep;
  role: UserRole;
  cartItems: number; // quanti articoli nel carrello
  doorIsClosed: boolean; // esempio di resource-based gating: stato di una risorsa specifica
}

Ora descriviamo quali tools esistono nel sistema e come filtrarli:

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',
];

Qui open_door è un esempio di strumento che dipende dallo stato di una risorsa specifica (porta chiusa o no).

E la funzione di gating vera e propria:

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;
}

Qui si vedono chiaramente tre «strati» di gating:

  • per step (byStep);
  • per ruolo dell’utente (withAdmin);
  • per stato della risorsa (withResources e flag doorIsClosed).

Non è codice di un SDK, ma uno schizzo architetturale. È così che si ragiona di solito sul tool gating: esiste un catalogo completo di strumenti e una funzione che, dato il contesto, restituisce un sottoinsieme.

5. Dove vive il tool gating nell’architettura dell’App

Colleghiamo un po’ la cosa a ciò che già sapete dello stack delle ChatGPT App.

Teoricamente il protocollo MCP funziona così

In MCP gli strumenti non devono essere cablati rigidamente in un JSON statico: il server può restituire l’elenco in modo dinamico in base alla sessione. Inoltre, nella specifica esiste un meccanismo di capabilities, con cui il server dichiara che il suo elenco di strumenti può cambiare, e la notifica tools/list_changed, in modo che il client (ChatGPT/agent) richieda di nuovo l’elenco dei tools quando qualcosa cambia.

Formalmente potete fare così, e alcuni client MCP funzioneranno con un elenco di tools dinamico. Ma ad oggi le ChatGPT App non supportano tools/list_changed. Forse in futuro cambierà, ma per ora questo approccio non funzionerà.

E funzionerà così

Conservate lo stato e l’elenco dei metodi disponibili lato modello. Potete semplicemente inviare al modello lo state e l’elenco dei tools disponibili a ogni step come parte della «rappresentazione del mondo»: nel prompt di sistema descrivete esplicitamente lo step corrente (per esempio, step = "browsing"), i flag chiave (per esempio, cartItems = 2, role = "user") e allegate solo il sottoinsieme di strumenti consentito in quel momento.

Il modello non sa «dimenticare» gli strumenti, ma segue molto bene istruzioni esplicite del tipo: «In questo step puoi usare solo queste funzioni…». In definitiva tutta la logica di gating per il modello appare come un semplice contratto: ecco lo stato corrente dello scenario, ecco l’elenco dei pulsanti che puoi usare; il resto per te non esiste. Non serve alcuna «magia» particolare — basta aggiornare coerentemente lo state e l’elenco dei tools nelle richieste al modello durante i passaggi tra gli step.

Inoltre, potete aggiungere istruzioni nello structuredContent, qualcosa del genere:

{
  "instructions": {
    "current_step": "browsing",
    "enabled_mcp_tools": ["search", "apply"]
  }
}

Potete anche aggiungere protezioni a livello del vostro business code. Anche se l’elenco dei tools è già «aggiornato», è importante raddoppiare la logica di gating negli stessi handler, perché:

  • il modello può dimenticare istruzioni e/o dati, se la conversazione è stata lunga;
  • il modello può provare a chiamare uno strumento «fantasma», che era disponibile allo step precedente;

Perciò il buon design è: «nascondiamo» gli strumenti al modello e, dentro l’handler, verifichiamo comunque se sia possibile eseguire l’azione in quel momento.

6. Tool gating a livello di modello vs a livello logico

Collegandolo alla sezione precedente: tutto ciò che accade al livello della chiamata al modello (quali flag/step mettete nel prompt) è gating a livello di modello, mentre i controlli negli stessi handler degli strumenti sono gating logico.

Ha normalmente senso separare due livelli:

  1. Gating a livello di modello — quando il modello sa che lo strumento è «consentito» proprio ora, perché scrivete esplicitamente nelle istruzioni quali funzioni sono disponibili in questo step. Per il modello il mondo appare così: «ecco lo state corrente, ecco questo set di pulsanti, gli altri non esistono».
  2. Gating logico — controlli all’interno dello strumento stesso. Anche se il modello prova comunque a chiamare checkout in anticipo (a causa della cache, di memoria fantasma o perché in uno dei passaggi precedenti gli avete mostrato quello strumento), l’handler guarda allo stato corrente e rifiuta educatamente: tipo «prima scegli un regalo, poi concludiamo l’ordine» (e non lancia semplicemente un’eccezione!).

Perché servono entrambi i livelli? Perché l’infrastruttura intorno alle LLM e gli stessi scenari possono comportarsi in modo non ideale:

  • il modello può ricordare di aver visto lo strumento checkout e provare a citarlo nei ragionamenti o persino in un tool‑call;
  • potete per errore passare in uno step un set di tools più ampio del necessario, e il modello comincerà a usare funzioni superflue;
  • client/layer intermedi possono mettere in cache la configurazione di chiamata del modello e per un po’ inviare il vecchio set di strumenti.

In pratica significa una cosa semplice: contare solo su «non abbiamo messo lo strumento nei tools — quindi non verrà mai più chiamato» è rischioso. I controlli negli handler servono comunque.

Esempio di gating logico nell’handler di checkout in 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.',
    };
  }

  // ... logica reale di checkout
}

Una risposta del genere aiuta sia l’utente, sia il modello: il modello vede un errore strutturato e può correggere il piano d’azione.

7. Come collegare il tool gating con UI e widget

Il tool gating non riguarda solo il server. Anche UI/UX deve percepire i cambiamenti.

Il widget conosce lo step corrente (abbiamo già parlato di widgetState e di come questo stato possa memorizzare, per esempio, currentStep). Anche il modello lo sa, perché lo step o viene passato esplicitamente agli strumenti, o è inserito nel prompt di sistema. È importante che UI e set di tools attivi siano sincronizzati.

Se il modello ritiene che lo step sia «Selezione», ma il widget mostra l’interfaccia «Intervista», l’utente si confonde. Se invece — la UI mostra già il pulsante «Paga», ma checkout non è ancora disponibile, il modello sarà in una situazione strana: il pulsante c’è, ma la funzione sembra «non funzionare».

Piccolo schema del ciclo di vita di uno step con tool gating:

flowchart TD
  A[L’utente compila l’intervista nel widget] --> B[Il widget chiama lo strumento save_preference / finish_interview]
  B --> C[MCP / backend aggiorna state.step]
  C --> D[Il server cambia il set di tools per la sessione]
  D --> E[Il client ChatGPT aggiorna i tools disponibili per il modello]
  E --> F[Il modello pone nuove domande
e/o chiama nuovi strumenti] C --> G[Il widget riceve il nuovo step tramite widgetState
e cambia la UI]

Per l’utente questo appare come un classico wizard: prima alcune domande, poi un elenco di regali, poi la conferma finale. Sotto il cofano si commutano contemporaneamente UI, elenco degli strumenti e istruzioni per il modello.

Nel widget Next.js questo si esprime molto semplicemente. Supponiamo che memorizziate step nel 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 />;
}

Qui non mostriamo i tools direttamente, ma diamo per scontato che il cambio di step nello stato sia allineato con il cambio dell’elenco di strumenti sul backend. Abbiamo visto come vivono gli step nel widget. Torniamo ora al lato del server MCP e vediamo come gli stessi step e lo stato del carrello influenzino l’elenco degli strumenti.

8. Esempio: tools/list dinamico sul server MCP

Avete già visto che il server MCP può mantenere lo stato di sessione e usarlo per prendere decisioni. In un approfondimento separato del caso GiftGenius è mostrato un esempio in cui lo stato step e il carrello (cart) risiedono in memoria oppure in Redis. Da essi dipende quali tools il server restituisce in risposta alla richiesta dell’elenco.

È possibile che, quando leggete questa lezione, le ChatGPT App supportino già toolChanged all’interno della sessione corrente. È semplicemente molto logico, quindi penso sia solo questione di tempo. In tal caso, ho per voi un breve excursus su come fare tool gating proprio usando gli strumenti nativi del protocollo MCP.

Riscriviamo l’idea in TypeScript (server MCP astratto):

interface SessionState {
  step: WorkflowStep;
  cartItems: number;
  doorIsClosed: boolean; // esempio di stato di una risorsa
}

const allTools: ToolDefinition[] = [/* completo set di strumenti */];

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));
}

E da qualche parte nell’handler di finish_interview cambiate lo step e segnalate al client che l’elenco dei tools è stato aggiornato:

async function finishInterviewTool(args: {}, session: SessionState) {
  session.step = 'browsing';
  await notifyToolsListChanged(); // chiamata ipotetica a notifica MCP

  return { success: true };
}

Su un MCP reale userete uno specifico SDK e formati di messaggi concreti, ma la logica rimane più o meno questa: avete cambiato lo stato → avete aggiornato l’elenco dei tools → avete notificato il client.

9. Il tool gating come strumento di sicurezza

Ribadiamo il tema della sicurezza, perché si perde facilmente tra i dettagli tecnici.

Quando fate tool gating, riducete automaticamente le conseguenze di:

  • iniezioni di prompt del tipo «ignora le regole e avvia subito il pagamento» — perché allo step di intervista il modello semplicemente non ha checkout tra le opzioni;
  • bug nella logica di business — perché anche se un ramo di codice non controlla a fondo lo stato, lo strumento può essere fisicamente non disponibile;
  • fughe di dati — perché gli strumenti da admin non finiscono nell’elenco per l’utente normale.

Nella documentazione del corso il tool gating è citato esplicitamente come una delle pratiche di applicazione del principio dei privilegi minimi nel contesto degli strumenti LLM, specialmente per il checkout e altri step sensibili.

Quindi non è solo un modo per «rendere il modello meno buggato» — è anche un vero layer di protezione.

10. Come allenarsi da soli

Per fissare i concetti, potete pensare al tool gating per uno qualunque dei vostri scenari. Per esempio:

  • app educativa: step di definizione dell’obiettivo, step di valutazione del livello attuale, step di costruzione del piano — a ciascuno i propri tools;
  • prenotazioni: ricerca delle opzioni, scelta della variante, conferma e pagamento — di nuovo tre set di strumenti diversi;
  • assistente corporate interno: ricerca documenti, richiesta di accesso, esecuzione di operazioni — elenco diverso per dipendente, manager e admin.

Molto utile disegnare su carta o in Miro una tabella «Step ↔ quali strumenti sono visibili ↔ quali sono nascosti» e, accanto a ogni step, formulare brevemente perché servono proprio quei tools e perché gli altri vanno nascosti.

11. Errori tipici nel lavorare con il tool gating

Errore n. 1: «Buttare fuori» tutti gli strumenti subito e sperare nel modello.
A volte lo sviluppatore pensa: «Il modello è intelligente, capirà da solo cosa chiamare e quando». In realtà questo porta a inquinare il contesto, aumentare i token e avere più tool‑call errati. Fa particolarmente male quando il modello chiama all’improvviso checkout o un altro strumento pericoloso solo perché è nella lista. Il tool gating serve proprio a impedire che ciò accada.

Errore n. 2: Pensare che nascondere lo strumento nell’elenco sia sufficiente.
Anche se il server MCP non restituisce più lo strumento in tools/list, il modello può «ricordarlo» dalla storia, e l’infrastruttura — mettere in cache il vecchio set di tools. Di conseguenza arriva la chiamata a uno strumento fantasma. Se l’handler non fa controlli logici, può eseguire l’azione «al momento sbagliato». Perciò il gating deve essere sia a livello dell’elenco dei tools, sia dentro gli handler.

Errore n. 3: Asincronia tra UI e set di strumenti.
Capita che il widget sia già passato allo step "checkout" e mostri un bel pulsante «Paga», ma lato MCP vi siete dimenticati di includere checkout nell’elenco degli strumenti disponibili. Il modello non capisce perché il pulsante ci sia ma lo strumento non sia disponibile e produce risposte strane. O viceversa: l’elenco dei tools è già cambiato, il modello è pronto a selezionare i regali, ma il widget fa ancora domande dell’intervista. Quando progettate il workflow, è importante aggiornare in sincronia sia lo stato della UI sia l’elenco degli strumenti.

Errore n. 4: Logica di gating troppo complessa.
A volte, ispirati dalle possibilità, si inizia a costruire quasi un diagramma BPMN completo con decine di stati e condizioni per ogni evenienza. Alla fine nemmeno l’autore capisce, dopo una settimana, perché uno strumento è disponibile solo di giovedì negli anni bisestili. Per la maggior parte delle App basta una semplice scala di step e regole comprensibili: per step, per ruolo dell’utente e per alcuni flag chiave nello stato.

Errore n. 5: Incastrare rigidamente il tool gating nel prompt senza supporto lato server.
A volte si cerca di risolvere tutto con parole nel prompt di sistema: «In questo step non usare lo strumento checkout» — senza però cambiare l’elenco reale degli strumenti né aggiungere controlli sul backend. A volte il modello obbedisce, a volte no, e avrete un comportamento instabile. Le istruzioni nel prompt sono utili, ma devono integrare — non sostituire — il gating tecnico lato infrastruttura.

Errore n. 6: Ignorare ruoli e permessi di accesso.
Nelle applicazioni con autenticazione spesso si dimentica che il tool gating deve considerare non solo lo step ma anche il ruolo. Di conseguenza, un utente senza privilegi di amministratore vede (o peggio, può chiamare) strumenti pensati per il supporto o il DevOps. Nel modulo sull’autorizzazione avete già visto come i permessi entrano nel contesto; qui è importante non dimenticare di usare queste informazioni nella scelta dei tools.

Errore n. 7: Assenza di monitoraggio dei tool‑call errati.
Se avete sbagliato qualcosa nel gating, il sintomo tipico è l’aumento di errori come «Tool not available», «MethodNotFound» o i vostri errori logici del tipo «Checkout is not available yet». Se non raccogliete statistiche su questi eventi, potreste non accorgervi a lungo che gli utenti sbattono regolarmente contro muri invisibili. Un semplice logging e contatori per tipo di errore aiutano molto a notare in tempo i problemi nel design del workflow e del gating.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION