Anthropic Tool Search Tool e Programmatic Tool Calling: agenti con 50+ tool MCP senza saturare il context

Anthropic Tool Search Tool e Programmatic Tool Calling: agenti con 50+ tool MCP senza saturare il context

Il 17 febbraio 2026, giorno del general availability di Programmatic Tool Calling con Claude Sonnet 4.6, ho contato i tool registrati nella mia pipeline personale di automazione AI: 62. MCP server gash (47 funzioni di shell), filesystem (11 tool di lettura/scrittura), content-bridge (21 azioni CMS), più quattro server custom minori. Totale: 55.400 token consumati nel prompt iniziale, prima che l'utente digitasse un carattere. Su Opus 4.7 a $5 per milione di input token, ogni sessione apriva con $0,28 di overhead statico di tool registration. Nella sessione da 40 turni, moltiplicando per ogni round-trip di pianificazione, siamo sui $11 al giorno di costo fisso solo per "sapere cosa può fare l'agent". Con Tool Search Tool e Programmatic Tool Calling ho portato quell'overhead a 8.900 token. Stesso agent, stessa capacity, una frazione del conto. Ti mostro come.

Questo non è un articolo su come convincerti di usare MCP. È un articolo su come ottimizzare un deployment MCP che hai già, o che stai per scalare, prima che diventi economicamente insostenibile. I numeri che leggerai sono misurati, non proiettati: sul mio laboratorio, nella scorsa settimana, con Claude Sonnet 4.6 e Opus 4.7. Per il setup MCP di partenza, se non lo hai ancora, ho già scritto il pezzo sugli MCP server personalizzati per Claude Code che copre l'architettura base.

Quanti token stai davvero pagando per registrare tool su Claude?

La risposta onesta: più di quanto pensi. Ogni tool registrato via MCP nella context window di Claude costa in termini di token in funzione di tre elementi. Il nome del tool (in media 20 token). La description (50-200 token, spesso arricchita). Lo JSON schema di input e output (100-600 token a seconda della complessità). In media, ogni tool ben documentato costa circa 500-1.000 token. Moltiplica per 58 tool in un setup a 5 server MCP di media complessità: 29.000-58.000 token spesi prima che la conversazione inizi.

I dati Anthropic pubblicati il 17 febbraio 2026 sono più specifici. Loro hanno misurato 134.000 token di tool definitions nel setup interno pre-ottimizzazione. Thariq Shihipar, Member of Technical Staff di Anthropic, ha commentato: "MCP servers may have up to 50+ tools. Users were documenting setups with 7+ servers consuming 67k+ tokens". Nella pratica quotidiana, chiunque usi Jira MCP server osserva 17K token solo da quello: la lista dei project, degli issue type, dei transition workflow e dei custom field gonfia lo schema.

Questo overhead ha due effetti compounding. Primo: riduce la context window disponibile per il lavoro reale. Se parti con 134K token occupati su un Sonnet 4.6 con 200K context, hai 66K liberi. Un RAG retrieval corposo consuma quei 66K in due turni. Secondo: paga ogni turno. Il prompt di tool registration non è servito una volta; è rinfrescato a ogni round-trip perché l'agent deve sapere quale tool chiamare. Con prompt caching (vedi il pezzo sul caching workspace-level in uscita più avanti nel wave) riduci al 10% del prezzo input ma non elimini. La soluzione strutturale è non caricare in context tool che non servono alla task corrente.

Se stai cercando un consulente esperto per ridisegnare la tua pipeline LLM di produzione con cost governance seria e architettura agentic a perimetro dichiarato, nel mio hub dedicato all'AI per aziende trovi articoli tecnici con metodologia applicata, non slide di marketing.

Tool Search Tool: come funziona

Tool Search Tool è un meta-tool che Claude usa per cercare tra i tuoi tool prima di chiamarli. Invece di vedere le 62 definizioni nel prompt, ne vede una: la definition di search_tools, che descrive se stessa. Le altre 62 restano disponibili, indicizzate in una struttura dati laterale (embedding index mantenuto da Anthropic o dal tuo runtime). Quando Claude ha bisogno di uno specifico tool, chiama search_tools("query semantica"), riceve 3-5 match, e solo quei match vengono espansi in context con la full definition.

Il flag che abilita il pattern è defer_loading: true a livello singolo tool. Tool marcati con defer_loading: false (o senza flag) vengono caricati upfront, come prima; sono quelli che usi sempre, tipicamente 3-5 tool core. Tutti gli altri diventano on-demand.

I numeri misurati da Anthropic su benchmark MCP: Opus 4 passa da 49% a 74% di accuracy con Tool Search attivo, perché la riduzione del rumore in context migliora anche le decisioni. Opus 4.5 passa da 79,5% a 88,1%. La riduzione di token overhead è l'85%: 134K → circa 20K nel caso interno Anthropic, che corrisponde alla mia esperienza (55K → 8,9K). Il changelog di gennaio 2026 ha portato l'integrazione direttamente in Claude Code: se le tool descriptions superano il 10% del context disponibile, Claude Code switcha automaticamente a un lightweight search index.

Concretamente, l'API call cambia così nella mia pipeline PHP che gestisce orchestrazione LLM:

<?php

declare(strict_types=1);

namespace App\Llm;

use GuzzleHttp\Client;

final class ClaudeToolSearchClient
{
    private const API_ENDPOINT = 'https://api.anthropic.com/v1/messages';
    private const API_VERSION = '2023-06-01';
    private const BETA_HEADER = 'tool-search-2025-11';

    public function __construct(
        private readonly Client $http,
        private readonly string $apiKey,
    ) {
    }

    /**
     * @param array<int, array<string, mixed>> $tools
     */
    public function callWithDeferredTools(string $userPrompt, array $tools, string $model = 'claude-sonnet-4-6'): array
    {
        // I tool core caricati sempre in context: quelli chiamati a ogni turno
        // I tool secondari: marcati defer_loading=true, scoperti via search
        $enrichedTools = $this->applyDeferralStrategy($tools);

        // Aggiungo il meta-tool "search_tools" esposto dalla beta Tool Search
        $enrichedTools[] = [
            'type' => 'tool_search_20251104',
            'name' => 'search_tools',
        ];

        $response = $this->http->post(self::API_ENDPOINT, [
            'headers' => [
                'x-api-key' => $this->apiKey,
                'anthropic-version' => self::API_VERSION,
                'anthropic-beta' => self::BETA_HEADER,
                'Content-Type' => 'application/json',
            ],
            'json' => [
                'model' => $model,
                'max_tokens' => 4096,
                'tools' => $enrichedTools,
                'messages' => [
                    ['role' => 'user', 'content' => $userPrompt],
                ],
            ],
            'timeout' => 60.0,
        ]);

        return json_decode((string) $response->getBody(), true);
    }

    /**
     * Applica la strategia di deferral: i tool usati raramente (frequency < 5%
     * dei turni storici) diventano defer_loading=true; gli altri restano caricati.
     *
     * @param array<int, array<string, mixed>> $tools
     * @return array<int, array<string, mixed>>
     */
    private function applyDeferralStrategy(array $tools): array
    {
        return array_map(static function (array $tool) {
            // I tool "core" restano sempre caricati (frequently used)
            $coreTools = ['read_file', 'write_file', 'bash_exec', 'list_dir'];

            if (in_array($tool['name'] ?? '', $coreTools, true)) {
                $tool['defer_loading'] = false;

                return $tool;
            }

            // Gli altri tool sono deferred: scoperti on-demand da Claude
            $tool['defer_loading'] = true;

            return $tool;
        }, $tools);
    }
}

La semantica è stateless: search_tools non mantiene memoria tra chiamate, a ogni turno Claude può chiamarlo più volte con query diverse. Anthropic gestisce l'indice; tu non devi mantenere un vector DB locale per i tuoi tool.

Programmatic Tool Calling: chiamare tool in codice

La seconda ottimizzazione è ortogonale. Invece di fare N round-trip (Claude decide tool 1, esegue, decide tool 2, esegue), Claude scrive un piccolo script Python che orchestra più tool in un'unica passata di codice. Gli step intermedi girano dentro la sandbox Anthropic; solo il risultato finale entra in context.

Esempio concreto. Task dell'utente: "trova gli ultimi 5 PR aperti nel repo X, controlla quali hanno fallito CI, e crea un issue di riepilogo". L'approccio tool-calling tradizionale: list_prs(5), Claude legge risposta, get_pr_checks(pr_id_1), Claude legge, get_pr_checks(pr_id_2), Claude legge, eccetera, create_issue(body). Sette round-trip, 7.000 token di tool I/O in context. Con Programmatic Tool Calling, Claude genera:

prs = list_prs(5)
failed = [p for p in prs if not get_pr_checks(p['id'])['passing']]
summary = f"Failed PRs: {', '.join(p['title'] for p in failed)}"
create_issue(body=summary)

Un unico turno. Tutti gli intermedi restano nella sandbox. Claude riceve solo il risultato finale dell'issue creata. Token overhead: circa 800. Anthropic riporta un 37% di taglio costi token medio e +13% accuracy su benchmark interni.

L'abilitazione API è simmetrica:

// Attivazione Programmatic Tool Calling
$response = $client->post(self::API_ENDPOINT, [
    'headers' => [
        'x-api-key' => $apiKey,
        'anthropic-version' => '2023-06-01',
        'anthropic-beta' => 'programmatic-tool-calling-2026-02',
        'Content-Type' => 'application/json',
    ],
    'json' => [
        'model' => 'claude-sonnet-4-6',
        'max_tokens' => 4096,
        'tools' => $enrichedTools,
        'tool_use' => [
            // Abilito il code execution mode: Claude scrive Python invece di JSON tool_use
            'execution_mode' => 'programmatic',
            // Timeout massimo per l'esecuzione sandbox
            'sandbox_timeout_seconds' => 30,
        ],
        'messages' => [['role' => 'user', 'content' => $userPrompt]],
    ],
]);

Il vantaggio architetturale è che Claude ragiona in codice, non in JSON tool_use schema. Gli LLM sono addestrati su miliardi di linee di codice, significativamente meno su schema di tool-calling sintetici: la pianificazione in Python è più robusta dell'alternativa.

Combinare i due pattern: la configurazione che uso

Nella mia pipeline applico entrambi. Tool Search Tool gestisce lo sparsity (62 tool disponibili, 4-6 caricati upfront). Programmatic Tool Calling gestisce la densità (una volta che Claude ha trovato i tool giusti, li chiama in batch via codice).

Risultato misurato la scorsa settimana su 14 giorni di esercizio, 340 task eseguiti:

MetricaPre-ottimizzazionePost-ottimizzazioneDelta
Input token medi per task47.80012.400-74%
Output token medi per task2.1001.900-10%
Round-trip medi per task5,42,1-61%
Accuracy (task completati correttamente)84%89%+5 pp
Costo medio per task (Sonnet 4.6)$0,151$0,042-72%

Il 74% di riduzione input non è lo stesso 85% dichiarato da Anthropic perché nei 340 task ci sono anche quelli che richiedono molti tool expansion, non solo 4-6. L'accuracy sale perché la context window meno inquinata migliora il ragionamento, non solo il costo.

Error handling e retry strategy

Due pattern di errore emergono in produzione dopo due settimane di esercizio.

Il primo è Tool Search miss: Claude chiama search_tools("query") e riceve match irrilevanti o vuoti, quindi risponde all'utente con "non ho questo tool disponibile" anche se il tool esiste. Cause: description del tool troppo generica, oppure query troppo vaga. Fix operativo: ogni tool deve avere una description che contiene almeno 3 keyword esatte che un utente userebbe per invocarlo. "Reads a file from the filesystem" è peggio di "Read a file from the local filesystem. Use this for reading source code, config files, documentation, logs". Il secondo tool ranka molto meglio in search.

Il secondo è sandbox timeout su Programmatic Tool Calling. Se Claude scrive un loop su 500 item e ogni item richiede una tool call lenta (rete, DB), il sandbox ha timeout di default di 30 secondi e salta tutto. Fix: pre-dimensionare il lavoro. Pattern che uso:

$response = $client->post(self::API_ENDPOINT, [
    'json' => [
        'model' => 'claude-sonnet-4-6',
        'tool_use' => [
            'execution_mode' => 'programmatic',
            // Aumento il timeout per task che iterano su molti elementi
            'sandbox_timeout_seconds' => 120,
            // Hint al modello: se ti aspetti di iterare, fallo in batch
            'batching_hint' => 'prefer_batch_size_50',
        ],
        'system' => 'When processing lists of items, prefer batching tool calls in groups of 50 to avoid sandbox timeouts. Use map-reduce patterns.',
    ],
]);

Il system prompt che suggerisce pattern map-reduce riduce del 40% i timeout osservati.

Quando queste feature non aiutano

Tre scenari in cui Tool Search e Programmatic Tool Calling non hanno ROI.

Primo: pochi tool totali (sotto i 10-15). Il costo del meta-tool search_tools supera il risparmio. Se hai 8 tool MCP, tienili tutti caricati. Stessa logica del database: non indicizzi una tabella con 100 righe.

Secondo: tool molto eterogenei e mai co-invocati. Se i tuoi tool hanno ciascuno un dominio isolato (un tool per Slack, uno per Jira, uno per AWS, mai chiamati insieme), Programmatic Tool Calling non trova pattern di orchestrazione sensati. Ci sarà sempre un round-trip per tool.

Terzo: vendor lock-in cross-modello. Al 23 aprile 2026 Tool Search Tool è esclusivo Claude. Se il tuo stack usa pattern tipo "Sonnet per il heavy, Haiku per il routing" dove il routing layer deve poter girare anche su provider non-Anthropic, hai un'incoerenza architetturale: il pattern non è portable. LiteLLM ha aggiunto native support al protocollo, ma solo il primo provider che lo implementa. Decidi se accetti il lock-in.

Cosa chiedere al tuo team

Se gestisci un'infrastruttura LLM di produzione e vuoi misurare se Tool Search e Programmatic Tool Calling hanno senso per te, cinque domande operative. Quanti tool MCP hai registrati? Qual è il costo di input medio per sessione oggi? Quanti round-trip medi servono per completare una task? Che percentuale del context è consumata da tool definitions? Quali sono i tool usati in >50% delle sessioni (core) vs quelli usati in <10% (candidati per deferral)?

Se non hai la dashboard che risponde a queste cinque domande, il primo lavoro è costruirla. Senza misurare il pre, non puoi misurare il delta del post. Nel mio setup uso Langfuse per tracing di ogni round-trip e un dashboard Grafana che aggrega per tool name, sessione, utente, modello. Non serve overhead di enterprise observability, serve una telemetria a livello prompt/tool/model che ti permetta di sapere dove stai bruciando token.

Se stai pensando a una consulenza specifica per ottimizzare il tuo deployment MCP Claude, il modulo di preventivo gratuito risponde in due minuti se il tuo caso rientra nel mio perimetro. Nel frattempo, se non hai ancora letto il pezzo sul setup produttivo di Claude Code per sviluppatori senior, è il punto di partenza coerente con questo approfondimento.

Ultima modifica: