IA & Développement

Cinquante outils, un seul contexte : charger les outils à la demande

Cinquante outils repartent en entier à chaque appel et le modèle choisit moins bien. Le catalogue en TypeScript nu, puis le tool search de l'API Claude.

Publié le 14 septembre 202614 min de lecture

« Décale mon point de jeudi et préviens Marc. »

Huit mots. Avant que le modèle les lise, l'application lui a envoyé autre chose : la description complète de chacun de ses outils. Le nom, ce qu'il fait, la forme exacte de ses paramètres. Pour un assistant qui gère un calendrier, une boîte mail, des fichiers, un CRM et des notes, ça fait une cinquantaine d'outils. La documentation de l'Agent SDK1 compte 10 000 à 20 000 tokens pour cinquante outils, soit une vingtaine de pages de texte, expédiées avant chaque réponse.

Sur ces cinquante, la demande en utilise deux.

Anthropic donne un ordre de grandeur dans son billet2 sur l'usage avancé des outils : cinq serveurs MCP, cinquante-huit outils, environ 55 000 tokens consommés avant que la conversation commence. La configuration est banale, GitHub, Slack, Sentry, Grafana et Splunk branchés ensemble. Ajoutez Jira, qui pèse à lui seul 17 000 tokens et vous approchez des 100 000.

Cet article déplie le problème, puis le pattern qui le règle : un catalogue à la place d'une liste et la définition complète d'un outil chargée au moment où le modèle en a besoin. D'abord en TypeScript nu, pour voir le mécanisme. Ensuite tel qu'Anthropic l'a intégré dans son API. C'est le genre d'arbitrage qu'on tranche tôt sur une prestation d'agents et enchaînements pilotés par des modèles de langage, avant que le catalogue d'outils ne grossisse sans contrôle.

Trois mots avant de commencer

  • une définition d'outil, c'est le mode d'emploi d'un bouton que le modèle peut appuyer : un nom, une phrase qui dit à quoi il sert et un schéma JSON qui décrit ses paramètres. Un outil calendrier_deplacer avec ses champs id, debut, fin et notifier tient en quelques lignes. Un outil de recherche dans un CRM, avec ses douze filtres, en prend beaucoup plus ;
  • la fenêtre de contexte, c'est tout ce que le modèle lit avant de répondre : les consignes, la conversation et les définitions d'outils. Elle est finie et elle se paie au token ;
  • un appel, c'est un tour de boucle. Le modèle ne garde rien entre deux appels : tout ce qu'il doit savoir repart en entier à chaque fois, définitions comprises.

Ce dernier point est celui qu'on oublie. On mesure le poids de ses outils une fois, au démarrage, alors qu'il faut le multiplier par le nombre d'appels que fait une conversation.

Ce que coûtent cinquante outils

Le premier coût est celui qu'on voit sur la facture. Dans l'exemple d'Anthropic2, les définitions d'une cinquantaine d'outils MCP pèsent environ 72 000 tokens et le contexte atteint 77 000 tokens avant que le moindre travail commence. Une conversation de dix tours renvoie ce bloc dix fois.

Le second coût ne se voit pas sur la facture et il est pire : le modèle choisit moins bien.

La documentation de l'API Claude3 affirme que la capacité du modèle à choisir le bon outil se dégrade au-delà de trente à cinquante outils disponibles. Elle ne cite pas d'étude, mais la recherche publiée va dans le même sens. Un article4 de mai 2026 mesure, sur le benchmark BFCL et ses 370 outils, qu'une sélection adaptative présentant sept outils en moyenne atteint la même couverture que d'en montrer cinquante, 90,3 % contre 90,8 %. Un autre, RAG-MCP5, fait passer la précision de sélection de 13,6 % à 43,1 % en filtrant les outils avant l'appel.

Plus il y a d'outils, plus il y en a qui se ressemblent. Anthropic cite notification-send-user et notification-send-channel2. Notre assistant a mail_envoyer, notes_partager et crm_contacter, qui font tous les trois « prévenir Marc » vus de loin. Et pendant que le modèle hésite entre eux, il lit vingt pages de descriptions dont dix-neuf ne servent à rien pour la demande en cours.

Compter avant de différer

Si les cinquante outils sont en réalité cinq familles de dix variantes du même geste, notes_ajouter_titre, notes_ajouter_liste, notes_ajouter_tableau et ainsi de suite, le problème est ailleurs. Je l'ai traité dans l'article sur l'architecture multi-agent : une famille se consolide en un seul outil dont un paramètre porte le type. La documentation d'Anthropic sur la définition des outils6 dit la même chose, moins d'outils plus capables réduisent l'ambiguïté de sélection.

Supposons la consolidation faite. Il reste cinquante outils réellement distincts, parce que l'assistant couvre cinq domaines qui n'ont rien en commun. Le calendrier ne partage aucun paramètre avec le CRM. C'est le cas que traite le chargement à la demande.

Le pattern : un catalogue à la place d'une liste

Le modèle reçoit au départ la liste de ce qui existe et un moyen d'aller chercher le mode d'emploi complet quand il en a besoin. Trois pièces :

  1. Un index compact. Pour chaque outil, le nom et une ligne. Cinquante lignes, quelques centaines de tokens. C'est tout ce qui entre dans le contexte au départ.
  2. Un outil de recherche. Le modèle lui donne une requête, il rend les noms des outils qui correspondent. C'est un outil comme un autre, sauf qu'il ne fait rien dans le monde.
  3. Un chargement différé. Quand un outil est trouvé, sa définition complète rejoint la conversation et y reste jusqu'à la fin. Au tour suivant, le modèle l'a sous la main sans rechercher.

Avant et après, pour notre assistant :

AVANT                                    APRÈS
  50 définitions complètes                 1 outil de recherche
  à chaque appel                           3 outils toujours chargés
                                             (les plus fréquents)
                                           1 index : 50 noms, une ligne chacun
  le modèle choisit parmi 50               + les outils trouvés,
                                             chargés une fois
                                           le modèle choisit parmi 5 ou 6

Anthropic chiffre le même avant et après2 sur son cas à cinquante outils MCP : environ 8 700 tokens de contexte au lieu de 77 000, soit 85 % de moins. Anthropic donne cet ordre de grandeur en exemple, sans le présenter comme un benchmark. Il tient chaque fois que la plupart des outils sont hors sujet pour une demande donnée.

En TypeScript, sans framework, le mécanisme tient en une quarantaine de lignes :

type ToolDef = { name: string; description: string; input_schema: object }

const catalog: ToolDef[] = [
  /* les 50 outils, definitions completes */
]
const alwaysOn = new Set(['calendrier_lister', 'mail_lire', 'notes_chercher'])

// 1. L'index : une ligne par outil, injectée dans le prompt système
const index = catalog
  .map((t) => `- ${t.name} : ${t.description.split('.')[0]}`)
  .join('\n')

// 2. L'outil de recherche : un outil ordinaire, sans effet de bord
const searchTool: ToolDef = {
  name: 'chercher_outil',
  description: 'Cherche un outil par mots-clés. Rend au plus cinq noms.',
  input_schema: {
    type: 'object',
    properties: { requete: { type: 'string' } },
    required: ['requete'],
  },
}

function search(requete: string): string[] {
  const mots = requete.toLowerCase().split(/\s+/)
  return catalog
    .map((t) => {
      const texte = `${t.name} ${t.description}`.toLowerCase()
      return { t, score: mots.filter((m) => texte.includes(m)).length }
    })
    .filter((x) => x.score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, 5)
    .map((x) => x.t.name)
}

// 3. Le chargement différé : l'ensemble visible grossit au fil des tours
const loaded = new Set(alwaysOn)

function visibleTools(): ToolDef[] {
  return [searchTool, ...catalog.filter((t) => loaded.has(t.name))]
}

// Dans la boucle, quand le modèle appelle chercher_outil :
//   const found = search(args.requete)
//   found.forEach((n) => loaded.add(n))
//   puis renvoyer found comme résultat d'outil.
//   Au tour suivant, visibleTools() contient les outils trouvés.

La recherche ici compte des mots. Un vrai système prend BM25, des embeddings, ou un petit modèle qui lit l'index et désigne les outils utiles. La documentation MCP sur les bonnes pratiques client7 liste ces quatre stratégies et recommande de basculer vers ce mode quand les définitions dépassent 1 à 5 % de la fenêtre de contexte.

Deux conséquences à garder en tête. Un outil trouvé reste chargé : la liste visible ne fait que grossir pendant la conversation, ce qui évite de relancer une recherche pour chaque message. Et la recherche coûte un aller-retour : le modèle appelle chercher_outil, reçoit des noms, puis appelle l'outil au tour d'après. Sur une conversation qui utilise dix outils, c'est un appel de plus. Sur un assistant qui n'en a que huit au total, c'est un appel de trop et il vaut mieux tout charger.

L'implémentation native dans l'API Claude

Anthropic a intégré ce pattern dans son API sous le nom de tool search tool. Annoncé en bêta le 24 novembre 2025, il est disponible sans en-tête bêta depuis le 17 février 2026, d'après les notes de version8, sur tous les modèles à partir de Claude 4.5 selon la documentation3.

Le principe est celui du paragraphe précédent, avec deux différences. La recherche tourne côté serveur et le chargement différé est un simple drapeau sur chaque outil.

import Anthropic from '@anthropic-ai/sdk'

const client = new Anthropic()

const response = await client.messages.create({
  model: 'claude-sonnet-5-5',
  max_tokens: 1024,
  tools: [
    // l'outil de recherche, fourni par l'API
    { type: 'tool_search_tool_bm25_20251119', name: 'tool_search_tool_bm25' },

    // les trois outils les plus fréquents, chargés d'office
    { name: 'calendrier_lister', description: '…', input_schema: { /* … */ } },
    { name: 'mail_lire', description: '…', input_schema: { /* … */ } },
    { name: 'notes_chercher', description: '…', input_schema: { /* … */ } },

    // les quarante-sept autres, différés
    {
      name: 'calendrier_deplacer',
      description: 'Déplace un événement et prévient les participants.',
      input_schema: { /* ... */ },
      defer_loading: true,
    },
    // ...
  ],
  messages: [
    { role: 'user', content: 'Décale mon point de jeudi et préviens Marc.' },
  ],
})

Ce qu'il faut savoir avant de s'en servir, tel que la documentation3 le décrit :

  • Vous envoyez toujours tout. Le drapeau defer_loading ne change pas ce que contient la requête, il change ce qui entre dans le contexte du modèle. Les cinquante définitions partent à chaque appel ; seules celles trouvées par la recherche sont montrées au modèle.
  • Deux variantes de recherche. tool_search_tool_regex : le modèle écrit un motif d'expression régulière, 200 caractères au plus. tool_search_tool_bm25 : il écrit une requête en langage naturel, 500 caractères au plus. Les deux cherchent dans les noms, les descriptions et les noms et descriptions des paramètres. Aucune ne remplace l'autre.
  • Au moins un outil reste chargé. Tout différer renvoie une erreur 400. Anthropic recommande de garder chargés les trois à cinq outils les plus utilisés.
  • Cinq résultats par défaut, réglable de 1 à 10 000. Le plafond est de 10 000 outils différés par requête.
  • La réponse contient des références. Le modèle appelle la recherche, l'API renvoie un bloc tool_search_tool_result avec des blocs tool_reference et les développe elle-même en définitions complètes avant le tour suivant. Vous n'avez rien à renvoyer pour cet appel.
  • Pas de facturation à part. La recherche n'est pas comptée comme un outil serveur. Les définitions qu'elle charge sont facturées en tokens d'entrée, comme n'importe quelle définition.

Le point le plus utile pour la facture est ailleurs : le cache de prompt est préservé9. Les outils différés ne font pas partie du préfixe mis en cache. Quand le modèle en découvre un, sa définition est ajoutée dans l'historique de la conversation et le préfixe ne bouge pas. C'est ce que l'implémentation maison du paragraphe précédent ne sait pas faire : chaque fois que votre tableau d'outils change, le préfixe change et le cache est perdu.

Sur la précision, Anthropic publie deux mesures internes2 sur ses évaluations MCP à grand catalogue : Opus 4 passe de 49 % à 74 % avec le tool search, Opus 4.5 de 79,5 % à 88,1 %.

Pour les outils qui viennent d'un serveur MCP branché via le connecteur de l'API10, le même drapeau se pose sur le serveur entier, ou outil par outil.

Le même pattern, ailleurs

Une fois qu'on le voit, on le retrouve partout, sous des noms différents.

Claude Code11 l'active par défaut pour les outils MCP : seuls les noms entrent au démarrage de la session. La variable ENABLE_TOOL_SEARCH=auto charge tout tant que les définitions pèsent moins de 10 % de la fenêtre et bascule en différé au-delà.

Mastra livre un ToolSearchProcessor12 depuis la version 1.2.0, publiée en février13, avec deux méta-outils, search_tools et load_tool et un classement14 BM25. Le changelog13 annonce une réduction d'environ 94 % du contexte sur les grands catalogues, sans mesure publiée. Le framework est présenté dans l'article sur Mastra 1.0.

Le Vercel AI SDK15 n'a pas de moteur de recherche propre, mais activeTools et prepareStep restreignent les outils visibles à chaque pas de la boucle et son fournisseur Anthropic expose le tool search natif via une option deferLoading16 par outil.

LangChain17 propose deux middlewares : l'un fait choisir les outils pertinents par un modèle secondaire avant l'appel principal, l'autre délègue la recherche au fournisseur.

Côté protocole, la spécification MCP18 n'a toujours pas de primitive de recherche : tools/list se pagine, un serveur peut signaler que sa liste a changé et depuis juillet 2026 il donne des indices de cache. Une proposition de filtre sur tools/list a été fermée sans suite19 en juin 2026. La roadmap20 du 22 août 2026 ouvre un chantier « progressive discovery » pour qu'un serveur puisse exposer une petite porte d'entrée et dévoiler son catalogue au fil de la conversation. En attendant, c'est à l'hôte de le faire, ou au fournisseur de modèle. Le rôle de MCP face à A2A est détaillé dans l'article sur les deux protocoles.

Il existe enfin une variante plus radicale, décrite par Anthropic dans « Code execution with MCP »21 : les outils sont présentés au modèle comme des fichiers dans une arborescence, un dossier par serveur, un fichier TypeScript par outil et le modèle lit ceux dont il a besoin. Sur leur exemple, un flux Google Drive vers Salesforce passe de 150 000 à 2 000 tokens. C'est le même principe, avec le système de fichiers comme index.

Ce que ça ne règle pas

Un outil mal nommé reste introuvable. La recherche lit les noms et les descriptions et rien d'autre. Un outil traiter avec une description d'une ligne ne sortira jamais pour « préviens Marc ». Anthropic recommande22 de préfixer les noms par domaine, calendrier_, mail_, crm_ et signale que le choix entre préfixe et suffixe a un effet mesurable sur leurs évaluations. Pour l'éditeur d'un SaaS, c'est devenu une question d'acquisition, que traite l'article sur l'entonnoir SaaS.

Le modèle doit savoir qu'un outil peut exister pour le chercher. Si rien dans le prompt ne dit que l'assistant sait toucher au CRM, il répondra qu'il ne peut pas prévenir Marc, sans jamais lancer de recherche. L'index d'une ligne par outil, ou au minimum une phrase par famille, sert à ça.

Le pattern ajoute un aller-retour et il ne consolide rien. Un catalogue de cinquante variantes du même geste reste un mauvais catalogue, avec ou sans recherche. Le premier geste reste de compter.

Ce qu'il faut retenir

Le modèle doit savoir que les outils existent et disposer d'un moyen d'aller chercher leur mode d'emploi : un index, une recherche, un chargement qui ne se fait qu'une fois. Le tool calling et les autres patterns de base sont dans le guide de conception d'un module agentique.

Sources

  1. Anthropic, Claude Agent SDK, Tool search, consulté le 29/10/2026.
  2. Anthropic, Introducing advanced tool use, 24/11/2025.
  3. Anthropic, Claude API, Tool search tool, consulté le 29/10/2026.
  4. Repantis et al., How Many Tools Should an LLM Agent See? A Chance-Corrected Answer, arXiv, 23/05/2026.
  5. Gan et Sun, RAG-MCP, arXiv, 06/05/2025.
  6. Anthropic, Claude API, Define tools, consulté le 29/10/2026.
  7. Model Context Protocol, Client best practices, consulté le 29/10/2026.
  8. Anthropic, Claude Developer Platform release notes, consulté le 29/10/2026.
  9. Anthropic, Claude API, Tool use with prompt caching, consulté le 29/10/2026.
  10. Anthropic, Claude API, MCP connector, consulté le 29/10/2026.
  11. Anthropic, Claude Code, Connect Claude Code to tools via MCP, consulté le 29/10/2026.
  12. Mastra, ToolSearchProcessor reference, consulté le 29/10/2026.
  13. Mastra, Changelog 2026-02-04, 04/02/2026.
  14. Mastra, pull request 12290, fusionnée le 31/01/2026.
  15. Vercel, AI SDK Core, Tools and tool calling, consulté le 29/10/2026.
  16. Vercel, AI SDK, Anthropic provider, consulté le 29/10/2026.
  17. LangChain, Built-in middleware, consulté le 29/10/2026.
  18. Model Context Protocol, Changelog de la spécification 2026-07-28, 28/07/2026.
  19. Model Context Protocol, issue 1821, Dynamic Tool Discovery, fermée le 24/06/2026.
  20. Model Context Protocol, The New MCP Roadmap, 22/08/2026.
  21. Anthropic, Code execution with MCP, 04/11/2025.
  22. Anthropic, Writing tools for agents, 11/09/2025.