- 2.1 — Concevoir des interfaces d'outils claires, avec des descriptions et des frontières nettes
- 2.2 — Implémenter des réponses d'erreur structurées pour les outils MCP
- 2.3 — Distribuer les outils entre les agents et configurer le choix d'outil
- 2.4 — Intégrer des serveurs MCP dans Claude Code et dans les workflows d'agents
- 2.5 — Sélectionner et employer efficacement les outils intégrés (Read, Write, Edit, Bash, Grep, Glob)
Ce que ce cours possède, ce qu’il renvoie ailleurs
D0 et D1 sont supposés acquis, et tous deux renvoient ici. D0 a nommé l'anatomie
d'un outil — name, description, input_schema — et a dit deux fois que
c'est en D2.1 que se développe la question du libellé qui fait choisir au
modèle le bon outil parmi plusieurs similaires, une fois pour le champ
tools, une fois pour le mot-clé du system prompt qui tire un outil sur chaque
tour. D1 a renvoyé la forme d'une charge utile d'erreur en D2.2 et le
cadrage des outils entre agents en D2.3. Ces trois dettes sont réglées
ci-dessous.
Le guide place ailleurs les notions voisines, et ce cours le respecte.
tool_choice est testé en TS 2.3 et en TS 4.3, et c'est D4 qui le possède :
2.3 énonce ce qu'il garantit pour la distribution d'outils et renvoie là-bas pour
le champ lui-même. La propagation d'une erreur entre un subagent et son
coordinateur relève du TS 5.3 ; ce cours possède la forme de l'erreur, D5
possède ce que le coordinateur en fait. Et ce qui fait charger une description
dans une session Claude Code — les fichiers .claude/agents/, allowedTools,
les settings — relève du domaine 3.
1. 2.1 — Concevoir des interfaces d'outils claires, avec des descriptions et des frontières nettes
Deux outils, tous deux correctement implémentés, tous deux connectés, tous deux testés. Et l'agent continue d'appeler le mauvais. Rien n'est cassé — le modèle a choisi à partir de la seule chose dont il disposait, et cette chose était deux phrases qui disaient la même chose.
C'est le premier point de connaissance du guide, et c'est la phrase sur laquelle
tout le domaine repose : les descriptions d'outils sont le mécanisme principal
qu'un LLM utilise pour sélectionner un outil, et des descriptions minimales
conduisent à une sélection peu fiable parmi des outils similaires. Le modèle ne
lit pas votre implémentation. Il lit name, description et input_schema, et
il décide.
La paire du guide lui-même est celle à mémoriser, parce qu'elle est délibérément banale :
{ "name": "analyze_content", "description": "Analyzes content and extracts key information." }
{ "name": "analyze_document", "description": "Analyzes documents and extracts key information." }
Rien ici n'est faux. Les deux phrases sont vraies. Et aucune propriété de l'une ou de l'autre ne permet à un modèle de préférer celle-ci à celle-là : la décision de routage retombe alors sur ce que la formulation de la requête fait résonner.
Ce qu'une description doit porter
Le guide nomme quatre choses qui y ont leur place — les formats d'entrée, des exemples de requêtes, les cas limites et l'explication des frontières — et la documentation de l'API pose un plancher de volume : viser trois à quatre phrases au minimum, couvrant ce que fait l'outil, quand l'utiliser et quand ne pas l'utiliser, ce que signifie chaque paramètre, ce qu'il retourne, et ses limites.
{
"name": "extract_web_results",
"description": "Extracts the ranked result list from a fetched search-engine
results page: title, URL, snippet and rank for each entry. Use this AFTER a
page has been retrieved, and only on a SERP — for the body text of an
ordinary article or a PDF, use extract_data_points instead. Returns an empty
list, not an error, when the page carries no results. Does not follow the
result links.",
"input_schema": {
"type": "object",
"properties": {
"html": { "type": "string", "description": "Raw HTML of the results page" },
"max_results": { "type": "integer", "description": "Cap on entries returned; default 10" }
},
"required": ["html"]
},
"input_examples": [
{ "html": "<html>…</html>", "max_results": 25 },
{ "html": "<html>…</html>" }
]
}
Deux détails de ce bloc coûtent peu et sont souvent négligés. input_examples
montre des appels types dont un où le paramètre optionnel est omis, et c'est
ainsi que le modèle apprend que l'omettre est légitime plutôt qu'un oubli. Et la
clause négative — « pour un article ordinaire, utiliser extract_data_points à
la place » — est l'explication de frontière : elle nomme l'outil voisin, si bien
que c'est la description qui lève l'ambiguïté au lieu de la laisser à la requête.
La frontière appartient à la description des deux outils, pas de l'un des deux. Enrichir un seul côté laisse l'autre tout aussi attirant pour les mêmes requêtes, et le misrouting change simplement de sens. Une paire d'outils se désambiguïse en tant que paire.
Les trois réparations, et le symptôme qui appelle chacune
Les compétences du guide sont trois gestes distincts. Lire l'énoncé pour savoir lequel il décrit est l'essentiel du travail sur ce task statement.
| Symptôme dans l'énoncé | Geste | L'exemple du guide |
|---|---|---|
| Deux outils dont les descriptions se recouvrent sémantiquement | Renommer et réécrire pour que le nom et la phrase disent ce qui est réellement retourné | analyze_content → extract_web_results, avec une description spécifique au web |
| Un outil générique employé bien au-delà de son périmètre | Remplacer par un outil contraint dont le contrat rejette le mésusage | fetch_url → load_document, développé en 2.3 |
| Un outil fourre-tout qui fait trois métiers sans rapport | Diviser en outils spécialisés aux contrats d'entrée/sortie définis | analyze_document → extract_data_points, summarize_content, verify_claim_against_source |
La division mérite un second regard, parce que c'est celle qui ressemble à du
travail en plus. Un outil nommé analyze_document n'a aucun contrat de sortie :
« analyser » peut vouloir dire un résumé, un tableau de chiffres ou un verdict
sur une affirmation, et le modèle choisit en devinant. Trois outils aux trois
noms portent chacun une promesse implicite sur ce qui revient, et c'est cette
promesse qui rend la sélection décidable.
Diviser n'est pas gratuit, et 2.3 est le contrepoids : un jeu d'outils qui dépasse quatre ou cinq dégrade la sélection pour une autre raison. On divise un outil parce que ses métiers ont des contrats de sortie différents, pas parce que plus d'outils fait plus rangé.
Nommer, pour que le nom travaille avec la description
Deux habitudes venues de la documentation de l'API, toutes deux mécaniques :
- Namespacer avec le service.
github_list_prs,slack_send_message. Unsearchnu dans une session qui tient quatre serveurs est une invitation au misrouting. - Regrouper les opérations liées derrière un paramètre
actionplutôt que de livrercreate_pr,review_pretmerge_pren trois outils. La même économie que réclame la règle de comptage de 2.3, appliquée dès la conception.
Le nom lui-même doit respecter ^[a-zA-Z0-9_-]{1,64}$, ce qui autorise
lettres, chiffres, tiret bas et tiret, jusqu'à soixante-quatre caractères.
Retourner du signal, pas du remplissage. Un outil qui répond par un mur de texte convenu dépense le contexte de l'agent en lignes dont aucune décision ne dépend. Retournez des identifiants sémantiques — UUID, slugs, noms — et laissez un appel de suivi aller chercher le reste. Rogner une sortie d'outil verbeuse est le territoire de D5 ; choisir ce qu'un outil retourne au départ est une décision de conception, et elle se prend ici.
Le system prompt peut l'emporter sur une bonne description
C'est le quatrième point de connaissance du guide et celui qu'on manque, parce que c'est du côté des définitions d'outils qu'on regarde. Une instruction sensible aux mots-clés dans le system prompt crée des associations d'outils non voulues.
# Le system prompt
You are a support assistant. ALWAYS verify the customer's identity before
answering anything.
Rien dans cette phrase ne nomme un outil. Mais « verify the customer » ressemble
assez à get_customer, et « always » porte plus loin que n'importe quelle
formulation propre à un tour : le modèle appelle désormais get_customer sur
des tours où l'identité n'est pas en jeu, y compris « quels sont vos horaires
d'ouverture ». Les descriptions d'outils sont bonnes. C'est l'instruction
au-dessus d'elles qui a routé le tour.
La compétence du guide est une relecture, pas une réécriture : relire le system prompt pour y trouver les instructions sensibles aux mots-clés qui pourraient l'emporter sur des descriptions d'outils bien écrites. Le correctif consiste à cadrer l'absolu — « vérifier l'identité du client avant de divulguer des données de commande ou de compte » — pour que le mot-clé cesse de s'attacher à chaque tour.
Lisez l'énoncé pour ce qu'il dit être pauvre, car les trois réparations du guide répondent à trois plaintes différentes.
« Les deux descriptions sont presque identiques » → renommer l'outil qui recouvre et réécrire les deux descriptions. « On demande à un outil trois choses sans rapport » → le diviser. « Une instruction du system prompt dit toujours » → la cause racine est le prompt, pas les descriptions.
Les distracteurs ici sont tous de l'ingénierie plausible, et chacun laisse
l'ambiguïté en place. Ajouter des exemples de routage few-shot au system prompt
coûte des tokens à chaque tour et ne touche jamais aux phrases sur lesquelles le
modèle sélectionne vraiment. Construire une couche de routage qui classe la
requête avant qu'aucun outil ne soit proposé remplace la compréhension du langage
naturel que le modèle a déjà par un composant que vous maintenez désormais.
Fusionner les deux outils en un lookup_entity générique est une architecture
défendable et un très grand pas quand le défaut immédiat tient à deux phrases
maigres. Et n'enrichir qu'une des deux descriptions laisse l'autre tout aussi
attirante : le misrouting persiste dans l'autre sens.
Écrire la description pour un lecteur humain. « Analyzes documents » en dit assez à un collègue, parce que le collègue ira lire le code. Le modèle, non.
Laisser le schéma porter le sens à lui seul. Un paramètre nommé mode avec
un enum de cinq valeurs et aucune description par valeur est une décision que
le modèle doit prendre sans information. Les descriptions de champs font partie
de la surface descriptive de l'outil. Avec le SDK Python MCP elles viennent du
Field(description=…) posé sur chaque paramètre, et le SDK construit le schéma
à partir des annotations de type qui les entourent.
Teste-toi sur cette section
Q1 (Logistics) — Two tool descriptions that read alike
Scenario: a delivery assistant routes "where is my parcel" requests to find_package 38% of the time, when they belong to find_shipment. find_shipment is described as "finds a shipment and returns its details", find_package as "finds a package and returns its details" — although find_package only returns warehouse scan events.
Question: what is the right fix?
A) Rename find_package to list_warehouse_scans and rewrite its description around the scan events.
B) Add a routing classifier that labels every request before any tool is offered.
C) Add worked routing examples to the assistant system prompt, teaching the distinction by demonstration.
D) Expand only the find_shipment description so delivery wording pulls those requests to it.
Answer: rename the overlapping tool and rewrite its description
Why: two near-identical descriptions leave the model nothing to discriminate on. The new name and the rewritten description finally say what that tool really returns — warehouse scan events, not a parcel location — so the functional overlap disappears at its source, where the selection decision is actually made.
Why the others are wrong:
- Over-engineering: it bypasses natural language understanding instead of repairing the ambiguity.
- Examples cost tokens on every turn and leave the ambiguous description in place.
- Improving one side keeps the other just as attractive for the same requests.
2. 2.2 — Implémenter des réponses d'erreur structurées pour les outils MCP
Un outil échoue. L'agent doit maintenant décider une chose : le rappeler, changer les arguments, prévenir l'utilisateur, ou passer la main à un humain. Chacune de ces décisions se prend à partir de la charge utile que l'outil a retournée — et voici ce que cette charge utile dit d'habitude :
{ "isError": true, "content": [{ "type": "text", "text": "Operation failed" }] }
Le point de connaissance du guide en est la conséquence : des réponses d'erreur uniformes empêchent l'agent de prendre les décisions de récupération appropriées. L'agent fera quand même quelque chose. Il réessaiera quatre fois un refus de permission, ou renoncera sur un timeout qui se serait dissipé à la deuxième tentative, parce que rien dans la charge utile ne les distingue.
isError est le drapeau que MCP vous donne : il dit à l'agent que l'appel a
échoué au lieu de retourner des données. Tout ce qui rend cet échec actionnable,
c'est vous qui le posez à côté.
{
"isError": true,
"content": [{
"type": "text",
"text": "{\"errorCategory\": \"transient\", \"isRetryable\": true, \"description\": \"Order service timed out after 5s. Retry in a few seconds.\", \"attempted_query\": \"order_id=12345\"}"
}]
}
errorCategory, isRetryable et la description lisible par un humain ne
sont pas des champs du protocole MCP. Ce sont une convention applicative, et
elles vivent à l'intérieur du texte du bloc de contenu, en prose ou en JSON
sérialisé. Ce que le protocole vous donne, c'est isError ; la structure, c'est
à vous de l'imposer, et le guide vous teste sur le fait de l'imposer de manière
cohérente.
Deux points de forme qui vont avec. content est un tableau de blocs de
contenu, jamais une chaîne nue ni un objet. Les métadonnées structurées
doivent donc vivre à l'intérieur d'un bloc, ce qui est précisément pourquoi on
les encode en texte. Et la même convention côté API Claude est un bloc
tool_result portant is_error: true, dont le content est ce que le modèle
lit pour décider de la suite. Même discipline, enveloppe différente.
Enfin, un détail d'orthographe qu'il vaut mieux neutraliser avant qu'il ne vous
coûte un point : le guide lui-même écrit à la fois isRetryable et
retriable, à deux puces d'intervalle dans le même bloc « Skills in ». Il
emploie isRetryable pour le booléen général et retriable: false pour les
violations de règle métier. Aucune des deux n'est un champ normalisé, donc
aucune ne peut être celle qui piège : ce sont des conventions applicatives
portées dans le contenu de l'erreur, comme errorCategory. Une option qui
emploie une graphie n'est pas fausse parce qu'elle n'a pas employé l'autre.
Deux mécanismes d'erreur, et un seul est le vôtre
Avant les catégories, une frontière que le protocole trace et dont un distracteur raffole. MCP signale les échecs à deux endroits différents, et ils ne sont pas interchangeables.
| Mécanisme | Signalé comme | Couvre |
|---|---|---|
| Erreur de protocole | Une erreur JSON-RPC standard — l'appel lui-même n'est pas passé | Outil inconnu, arguments invalides, panne du serveur |
| Erreur d'exécution d'outil | Un résultat réussi portant isError: true |
Échec d'API, données d'entrée invalides, violation de règle métier |
Relisez la seconde ligne deux fois, parce qu'elle est contre-intuitive et
qu'elle résume à elle seule ce task statement. Une règle métier violée n'est pas une
erreur de protocole. L'appel est parvenu à l'outil, l'outil a tourné, et il a
décidé que l'opération ne devait pas avoir lieu. La réponse revient donc comme
un résultat normal dont isError vaut vrai et dont l'agent peut lire le
contenu. Une réservation au-delà de l'horizon de 90 jours n'est pas une requête
malformée : c'est une requête que l'outil a comprise et refusée.
C'est pourquoi tout ce qui suit porte sur le contenu d'un résultat plutôt que sur des codes d'erreur. Les erreurs de protocole sont l'affaire du transport et vous n'avez rien à y concevoir. Les erreurs d'exécution sont les vôtres, et la décision de récupération de l'agent se prend entièrement à partir de ce que vous y mettez.
Les quatre catégories, et la décision que chacune rend possible
errorCategory |
Exemples | isRetryable |
Ce que l'agent doit faire |
|---|---|---|---|
transient |
Timeout, 503, panne réseau | true |
Réessayer localement ; ne propager que si l'échec persiste |
validation |
Entrée invalide, champ requis manquant | true, une fois l'entrée corrigée |
Corriger les arguments et rappeler |
business |
Violation de politique, seuil dépassé | false |
Expliquer à l'utilisateur ; proposer une alternative |
permission |
Accès refusé, privilège insuffisant | false |
Escalader vers un humain ; réessayer ne change rien |
Les deux colonnes de droite sont tout l'enjeu. La retryabilité est une affirmation sur la possibilité qu'une répétition de l'appel réussisse, et elle est connaissable au niveau de l'outil, pas au niveau de l'agent. Des métadonnées structurées évitent les tentatives de retry gaspillées. C'est la formulation du guide, et le gaspillage se facture à la tentative.
La ligne la plus mal lue est validation. Elle est retryable, et son retry
n'est pas une répétition : l'agent répare ses propres arguments et rappelle. La
classer à côté de permission parce que « l'appel a échoué pour une raison que
l'outil a donnée » vous coûte une récupération que l'agent pouvait faire seul.
Les erreurs métier réclament une phrase qu'un client peut entendre
Le guide demande deux choses sur une violation de règle métier : retriable: false et une explication compréhensible par le client. La seconde n'est pas de
l'ornement. L'agent va relayer cela à quelqu'un, et « BOOKING_RULE_4171 » se
relaie mal.
{
"isError": true,
"content": [{
"type": "text",
"text": "{\"errorCategory\": \"business\", \"retriable\": false, \"description\": \"Appointments cannot be booked more than 90 days ahead. The earliest available slot inside that window is 12 March.\", \"policy\": \"booking_horizon_90d\"}"
}]
}
Le code lisible par la machine reste, pour les journaux et pour le routage. La phrase posée à côté est ce qui permet à l'agent de répondre au client sans inventer la règle.
Un échec d'accès n'est pas un résultat vide
La dernière compétence du guide sur ce task statement est une distinction, et c'est celle qui produit des réponses fausses silencieuses plutôt que bruyantes.
| Ce qui s'est passé | Charge utile correcte | |
|---|---|---|
| Échec d'accès | La requête n'a jamais tourné — timeout, 503, connexion refusée | isError: true, catégorie transient, isRetryable: true |
| Résultat vide valide | La requête a tourné et n'a rien trouvé | Ce n'est pas une erreur. Un succès portant une liste vide |
Confondez-les et vous obtenez l'un des deux défauts. Signalez le résultat vide comme une erreur et l'agent réessaie une question à laquelle il a déjà été répondu. Signalez le timeout comme un résultat vide et l'agent conclut, de bonne foi, qu'il n'y a rien à trouver, et le trou n'apparaît jamais dans la réponse finale.
// Pas une erreur : le catalogue a bien été interrogé, il ne contient aucune correspondance.
{ "isError": false, "content": [{ "type": "text",
"text": "{\"results\": [], \"searched\": \"parts_catalog\", \"query\": \"part_no=88-2210\"}" }] }
Récupération locale, et ce qui franchit la frontière
Le guide demande une récupération locale des erreurs à l'intérieur des subagents : un subagent traite lui-même ses échecs transitoires et ne propage au coordinateur que ce qu'il ne peut pas résoudre localement, accompagné de ses résultats partiels et de ce qu'il a tenté.
{
"status": "partial",
"completed": ["order_lookup", "customer_profile"],
"failed": [{
"step": "shipping_status",
"errorCategory": "permission",
"isRetryable": false,
"attempted": "carrier_api.track(tracking_id=1Z999) — 3 attempts, 403 each time",
"description": "The carrier account has no tracking scope. A human must grant it."
}]
}
Trois propriétés rendent cette charge utile exploitable en amont : les résultats
partiels survivent, l'étape en échec nomme sa catégorie pour que le coordinateur
ne retente pas un refus de permission, et attempted dit ce qui a déjà été
essayé pour que le budget de retry ne soit pas dépensé deux fois sur le même
appel.
Où ce cours s'arrête. La forme de l'erreur — le drapeau, la catégorie, la retryabilité, la phrase adressée au client, l'enveloppe à résultats partiels — est testée ici, en TS 2.2. Ce qu'un coordinateur fait quand cette enveloppe lui arrive — continuer sur des résultats partiels, annoter la sortie finale de ce qui est resté non couvert, décider quand escalader — relève de la propagation d'erreurs entre agents, testée en TS 5.3 et développée en D5. Le TS 1.2 de D1 avait déjà posé la règle de routage et renvoyait ici pour la charge utile.
La charge utile générique "Operation failed" est l'anti-pattern autour duquel
ce task statement est bâti, et la question demande d'ordinaire pourquoi elle
bloque l'agent plutôt que par quoi la remplacer. La réponse nomme une décision
que l'agent ne peut plus prendre : l'appel vaut-il d'être répété, et a-t-il
vraiment reçu un résultat vide ou n'est-il jamais passé ?
Les distracteurs adoucissent le défaut au lieu de le réparer. Dire qu'un timeout
est « un délai, pas un vrai échec » et retirer le drapeau masque l'échec
entièrement. Qualifier la charge utile de malformée se trompe de défaut : elle
se parse et parvient intacte à l'agent ; elle est vide de métadonnées de
décision, pas cassée. Reprocher au message d'être trop court pour un humain vise
le mauvais lecteur. Et affirmer que les quatre catégories viennent de la
spécification MCP inverse l'arrangement réel : isError appartient au
protocole, les catégories sont les vôtres.
Lever une exception brute et laisser le framework la sérialiser en chaîne.
Un ValueError Python remonté par le SDK devient un message, et un message sans
catégorie est un "Operation failed" avec plus de mots. Attrapez-le, classez-le,
et retournez la charge utile structurée.
Propager au coordinateur chaque échec transitoire. Cela convertit un retry que le subagent pouvait faire en silence en un aller-retour, et le coordinateur a moins de contexte pour décider que le subagent qui a passé l'appel.
Teste-toi sur cette section
Q3 (Field Service) — One error payload for two different outcomes
Scenario: the search_parts tool answers {"isError": true, "content": [{"type": "text", "text": "Operation failed"}]} both when the supplier catalog times out and when the requested part number matches nothing in stock.
Question: why does this design block the agent?
A) It cannot tell a valid empty result from an access failure, so retrying is guesswork.
B) A timeout is a delay, not a real failure, so the error flag should stay unset.
C) The payload is malformed JSON, so the runtime discards it before the agent ever sees a result and the failure never reaches the retry logic at all.
D) The message is too short for the technician to act on in the field.
Answer: an empty result and an access failure become indistinguishable
Why: the agent decides from two signals — whether the call is worth repeating (isRetryable) and what it really received. A transient timeout calls for a retry, an empty catalog hit is a legitimate success. Merged into one payload, the agent can neither justify a retry nor state that nothing was found, and the run ends in wasted retries or silent gaps in the answer.
Why the others are wrong:
- The timeout is a genuine failure and the flag belongs there; what is missing is everything around it.
- The payload parses and reaches the agent intact — nothing is discarded on the way. Its shape could be tighter, but reformatting alone would not make the message actionable.
- Length is not the problem; the absence of decision metadata is.
Q5 (Scheduling) — What an actionable error response carries (Select the 2 correct answers.)
Scenario: you are designing the error responses of the book_appointment MCP tool so the agent can decide what to do next.
Question: which statements are accurate?
A) Returning {"isError": true, "content": "Booking failed"} already tells the agent enough to decide on a retry.
B) A calendar backend answering 503 is a transient failure and should carry isRetryable: true encoded in the text block of the content.
C) A validation failure — a required field left empty — is non-retryable like a permission denial, so the agent must hand the call over to a human.
D) The transient, validation, business and permission categories come from the official MCP specification.
E) A slot search that legitimately finds no availability must be reported differently from a backend timeout.
Answers: the retryability signal, and the split between an empty result and a failure
Why: the agent needs two things — whether the failure is worth repeating, and what it actually received. A valid search with no free slot is a success; a timeout demands a retry decision. Conflating them yields either pointless retries or silent gaps in the final report.
Why the others are wrong:
- That is the anti-pattern itself: a uniform error carries no basis for any decision.
- A missing required field is the textbook
validationcase, and that category is retryable once the argument is fixed: the agent repairs its own input and calls again. Handing over to a human with no retry is thepermissionrow. - They are an application-level convention encoded in the text block, not a formal part of the MCP spec.
3. 2.3 — Distribuer les outils entre les agents et configurer le choix d'outil
Chaque outil que vous ajoutez à un agent est une branche qu'il doit évaluer à chaque tour. Le guide met un chiffre là où cela cesse de fonctionner : donner à un agent l'accès à trop d'outils — 18 au lieu de 4-5 — dégrade la fiabilité de la sélection d'outil en augmentant la complexité de la décision.
C'est la dette de D1, et il vaut la peine d'être précis sur ce qu'elle affirme. Rien ne se dégrade dans le modèle ; ce qui se dégrade, c'est la décision. Dix-huit descriptions, dont certaines voisines, produisent un problème de sélection qu'aucune description individuelle ne peut résoudre, ce qui explique pourquoi le remède de 2.1 est la mauvaise réponse ici, et pourquoi lire l'énoncé compte plus que connaître les deux remèdes.
Les deux task statements partagent un symptôme — le mauvais outil est appelé — et se séparent sur l'indice que l'énoncé vous donne.
« Les descriptions sont minimales », « les deux se lisent pareil » → c'est 2.1 : réécrire, renommer, diviser. « L'agent dispose de dix-huit outils », « il porte des outils de trois rôles différents » → c'est 2.3 : ramener le jeu au rôle.
Réécrire les descriptions alors que l'énoncé a compté les outils est le distracteur qui attrape ceux qui n'ont appris qu'une des deux réponses.
L'accès aux outils cadré sur le rôle
Le deuxième point de connaissance explique ce qui se passe mal au-delà du nombre : les agents dotés d'outils hors de leur spécialité tendent à en faire un mauvais usage. Le cas du guide lui-même est un agent de synthèse qui se met à faire des recherches web. L'outil est là, le tour est difficile, et y recourir est un geste localement sensé qui produit un travail que personne n'a demandé.
Chaque subagent reçoit donc les outils dont son rôle a besoin, et pas d'autres :
| Subagent | Outils | Volontairement absents |
|---|---|---|
| Recherche web | web_search, load_document |
Tout ce qui écrit |
| Analyse de documents | load_document, extract_data_points, summarize_content |
web_search — hors de son rôle |
| Synthèse | read_findings, write_report, verify_fact |
web_search — voir ci-dessous |
Un outil que vous laissez de côté n'est pas dans la session de l'agent du tout. Il n'y a pas d'instruction à ignorer ni de refus à journaliser : l'agent travaille simplement sans lui. C'est ce qui rend l'omission plus forte que l'instruction.
Contraindre l'outil plutôt qu'instruire autour
La deuxième compétence du guide est une substitution : remplacer fetch_url
par load_document, qui valide que l'URL pointe vers un document. Le scénario
qui la motive est un subagent d'analyse de documents qui, disposant d'un outil
capable de récupérer n'importe quelle URL, finit par télécharger des pages de
résultats de moteur de recherche au lieu de documents.
{
"name": "load_document",
"description": "Fetches a document by URL and returns its text. Accepts PDF,
DOCX, TXT and Markdown URLs only; rejects any other URL with a validation
error. Use this to read a document you already have the address of — it does
not search, and it does not follow links inside the document.",
"input_schema": {
"type": "object",
"properties": {
"url": { "type": "string", "description": "Direct URL to a .pdf, .docx, .txt or .md file" }
},
"required": ["url"]
}
}
Une contrainte à l'interface l'emporte sur une contrainte dans le prompt.
« Ne récupère que des documents » est une instruction que le modèle met en
balance avec tout le reste de son contexte. Un outil qui rejette une URL non
documentaire est un contrat : le mauvais appel cesse d'être découragé et devient
impossible, et il retourne une erreur validation sur laquelle l'agent peut
agir, soit 2.2 qui arrive comme conséquence de conception.
Les outils cross-role, cadrés plutôt qu'ouverts
Le cadrage strict a un coût, et le guide en nomme le cas : l'agent de synthèse a constamment besoin de vérifier une affirmation ponctuelle. Renvoyer chaque vérification par le coordinateur — qui invoque l'agent de recherche web, attend, et relaie la réponse — paie deux ou trois allers-retours pour ce qui est le plus souvent une date ou un nom.
La réponse du guide n'est ni « donnez-lui la recherche web » ni « continuez à tout renvoyer ». C'est un outil cross-role limité, pour un besoin fréquent et précis :
{
"name": "verify_fact",
"description": "Verifies one specific claim against a named source document
and returns a confidence level (high/medium/low) with the supporting
excerpt. Use this only to check a claim you can already state and whose
source you can already name — for open-ended research, route the request
through the coordinator to the web research agent.",
"input_schema": {
"type": "object",
"properties": {
"claim": { "type": "string", "description": "The single claim to verify" },
"source_id": { "type": "string", "description": "Id of the source document to check against" }
},
"required": ["claim", "source_id"]
}
}
Deux propriétés en font un outil cross-role cadré plutôt qu'un trou dans le cadrage. Il est plus étroit en capacité que l'outil qu'il remplace : il confronte une affirmation à une source nommée, il ne cherche pas. Et sa description dit où s'arrête son mandat, si bien que les cas complexes continuent de passer par le coordinateur. Le cas simple et fréquent obtient un chemin direct ; le reste préserve l'architecture.
tool_choice, et ce qu'il garantit ici
tool_choice est le champ de requête qui décide si appeler un outil est
facultatif, obligatoire, ou obligatoire et nommé. Le champ lui-même — ses
quatre valeurs, leur comportement sous extended thinking, et leur usage pour la
sortie structurée — est enseigné en D4.3, qui le possède. Ce qui appartient à ce
task statement, ce sont les deux usages que le guide nomme, et une chose qu'il ne
répare pas.
| Usage du guide | Valeur | Pourquoi |
|---|---|---|
| Garantir un appel d'outil plutôt qu'un texte conversationnel | {"type": "any"} |
Le modèle doit appeler un outil ; il choisit encore lequel |
| Forcer un outil précis à s'exécuter en premier | {"type": "tool", "name": "extract_metadata"} |
Épingle cet outil-là pour le tour ; les étapes d'enrichissement viennent aux tours suivants |
{
"tools": [ /* extract_metadata, enrich_entities, score_relevance */ ],
"tool_choice": { "type": "tool", "name": "extract_metadata" }
}
Notez ce que l'appel forcé achète et ce qu'il n'achète pas. Il fixe la première action du tour. Séquencer le reste est affaire de traiter le résultat et d'émettre une autre requête. L'ordonnancement vit dans votre boucle, pas dans un paramètre unique.
tool_choice: {"type": "any"} garantit qu'un outil est appelé. Il ne garantit
pas que ce soit le bon, et il ne dit rien de la forme de ce qui revient. Une
option qui le propose comme remède à un scénario de misrouting répond à un
problème de sélection par une contrainte d'appel.
La sélection forcée est la réponse quand l'énoncé nomme un ordre : « le screening doit tourner avant tout le reste », « les métadonnées doivent être extraites avant l'enrichissement ». Une phrase dans la description d'outil disant qu'il doit toujours passer en premier reste probabiliste là où le paramètre est déterministe.
Donner tous les outils à un agent « au cas où ». Cela n'achète rien dont l'agent avait besoin et cela coûte la fiabilité de sélection de tout ce dont il avait besoin, plus le mésusage que le guide prédit pour les outils hors du rôle.
Fusionner les outils en un point d'entrée générique à paramètre mode pour
faire baisser le compte. Le compte baisse et la décision ne disparaît pas :
elle se déplace dans un argument, où les descriptions d'outils ne peuvent plus la
guider.
Accumuler les vérifications de l'agent de synthèse et les envoyer au coordinateur en fin de passe. Cela supprime les allers-retours et cela veut dire aussi que la synthèse a été écrite avant que quoi que ce soit n'y ait été vérifié.
Teste-toi sur cette section
Q2 (Data Platform) — A subagent holding a tool that runs anything
Scenario: a reporting subagent owns execute_sql, which accepts any statement against the warehouse. Traces show it browsing information_schema and joining raw event tables instead of reading the curated reporting views.
Question: what is the right fix?
A) Replace execute_sql with run_report_query, which takes a view name plus filters and rejects anything else.
B) Remove execute_sql and route every query through the coordinator, which already knows which curated views to read.
C) Keep the tool and add a denylist of raw table names to its handler.
D) Instruct the subagent in its system prompt to read only the curated views.
Answer: swap the generic tool for a constrained one
Why: a constrained replacement applies least privilege at the interface — the wrong call becomes impossible rather than discouraged.
Why the others are wrong:
- Adds a coordination round trip to a frequent and legitimate need of the subagent.
- A denylist enumerates known bad names and goes stale as soon as a new raw table ships.
- A prompt instruction stays probabilistic where a tool contract is deterministic.
Q4 (Compliance) — A screening step that must never be skipped
Scenario: a compliance assistant must call screen_sanctions on the counterparty before it produces anything else on that turn.
Question: which tool_choice value guarantees it?
A) {"type": "auto"}
B) {"type": "any"}
C) {"type": "tool", "name": "screen_sanctions"}
D) A sentence in the tool description stating that it must always run first.
Answer: forced selection naming the screening tool
Why: forced selection pins that exact tool for the turn. The rest of the workflow proceeds on later turns, once the screening result sits in context.
Why the others are wrong:
- The default mode lets Claude answer with no tool call at all.
- It forces a call but leaves the choice of tool open, so ordering is not guaranteed.
- Description text stays probabilistic where an API parameter is deterministic.
4. 2.4 — Intégrer des serveurs MCP dans Claude Code et dans les workflows d'agents
Tout ce que ce task statement configure est une connexion à un serveur MCP. MCP déplace la charge de la définition et de l'exécution des outils hors de votre application, vers des serveurs dédiés. Sans lui, exposer GitHub à Claude signifie écrire, tester et maintenir un schéma et une fonction pour chaque opération sur les dépôts, les pull requests et les issues que vous voulez couvrir. Avec lui, vous vous connectez à un serveur MCP GitHub qui les porte déjà.
C'est aussi la réponse à la question que le cours signale comme confusion courante : MCP et le tool use ne sont pas la même chose. Le tool use, c'est la manière dont Claude appelle un outil. MCP, c'est d'où l'outil vient : quelqu'un d'autre l'a déjà écrit.
Les deux portées
Un serveur se déclare à l'une de deux portées, et le choix tient à une seule question : est-ce que toute l'équipe en a besoin ?
| Portée | Fichier | Partagé ? | Pour |
|---|---|---|---|
| Projet | .mcp.json à la racine du dépôt |
Oui — suivi par le VCS, présent après un clone | Outillage d'équipe : Jira, GitHub, la base de connaissances interne |
| Utilisateur | ~/.claude.json |
Non — local à la machine | Serveurs personnels ou expérimentaux : vos notes, un prototype |
claude mcp add --scope user écrit dans ce second fichier : la commande et son
effet sont le même fait vu de deux côtés, et sur ce point le libellé du guide et
le comportement de l'outil concordent.
Les deux coexistent. Les outils de tous les serveurs MCP configurés sont
découverts à la connexion et disponibles simultanément à l'agent, quelle que
soit la portée qui les a déclarés. Mettre un serveur personnel dans
~/.claude.json ne vous coupe pas des serveurs projet. Vous avez l'union.
Cette propriété de découverte-à-la-connexion mérite d'être tenue comme un
mécanisme et pas seulement comme un fait. Le client demande à chaque serveur ce
qu'il fournit — un ListToolsRequest, auquel répond un ListToolsResult — et
les définitions d'outils collectées sont ce qui accompagne la requête de
l'utilisateur jusqu'à Claude. Rien n'est découvert paresseusement en cours de
tour : un serveur qui n'a pas démarré n'a simplement aucun outil dans le jeu, et
l'agent travaille sans eux exactement comme si vous ne l'aviez jamais déclaré.
Les secrets, par substitution de variables d'environnement
.mcp.json est suivi par le VCS. Les tokens, non. Le mécanisme du guide est la
substitution de variables d'environnement :
{
"mcpServers": {
"github": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
}
}
}
${GITHUB_TOKEN} se résout au lancement, depuis l'environnement propre au
développeur. Le fichier peut être committé et partagé ; chaque coéquipier
fournit son propre credential, et le dépôt n'en porte jamais aucun. type dit
comment le serveur est joint : "stdio" pour un processus lancé par command,
"http" pour un serveur joint à une url.
Deux propriétés de la substitution méritent d'être retenues. Elle accepte une
valeur par défaut, écrite ${VAR:-default}, ce qui permet à un réglage
optionnel d'être livré avec une valeur pendant qu'un credential reste
obligatoire. Et elle s'applique à plus que env : command, args, env,
url et headers y passent tous, ce qui permet à l'adresse d'un serveur ou à
son en-tête d'autorisation d'être eux aussi propres à chaque développeur.
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}",
"GITHUB_API_URL": "${GITHUB_API_URL:-https://api.github.com}"
}
Une variable non définie n'empêche pas le serveur de charger. Sans valeur
dans l'environnement et sans valeur par défaut, Claude Code avertit puis démarre
quand même le serveur, en laissant passer le texte littéral ${GITHUB_TOKEN}.
Rien n'échoue au moment de la configuration ; le serveur monte et chacun de ses
appels part sans authentification.
C'est le piège d'exploitation de ce mécanisme, et c'est pourquoi le symptôme à
reconnaître est une configuration qui fonctionne et dont tous les outils
retournent des erreurs d'authentification. Le correctif est dans l'environnement
du développeur, pas dans .mcp.json.
Un token écrit en clair dans un .mcp.json versionné. Le supprimer plus
tard n'y change rien : l'historique git le garde. Le credential doit être
considéré comme compromis et renouvelé. ${VAR} ou le ~/.claude.json non
versionné sont les deux endroits légitimes.
Un serveur personnel, spécifique à une machine, ajouté au .mcp.json
versionné, pointant vers un chemin absolu sous le répertoire personnel de
quelqu'un ou vers un binaire que personne d'autre n'a. Chaque clone hérite d'un
serveur qui ne peut pas démarrer. Il a sa place dans ~/.claude.json, où il
charge toujours pour son auteur et n'atteint personne d'autre.
Les resources, et pourquoi elles existent à côté des tools
Les serveurs MCP exposent trois primitives, et le guide est explicite sur les deux qu'il teste. Son annexe nomme les MCP tools et les MCP resources ; la liste in-scope dit « MCP tool and resource design ». Les prompts figurent ici pour que vous les reconnaissiez comme distracteur.
| Primitive | Contrôlée par | Objectif | Y recourir quand |
|---|---|---|---|
| Tools | Le modèle | Donner à Claude une capacité qu'il peut invoquer | Claude doit faire quelque chose |
| Resources | L'application | Exposer des données en lecture | Votre application a besoin de données pour son interface ou son prompt |
| Prompts | L'utilisateur | Des workflows prédéfinis derrière une commande slash ou un bouton | Non testé par cet examen |
Une resource est plus proche d'un handler HTTP GET que d'un outil : une URI
entre, des données sortent, et c'est votre application qui décide quand la
lire. Le modèle ne l'appelle pas. Une resource directe a une URI statique ;
une resource templatée porte des paramètres dans l'URI, que le SDK analyse et
passe à votre fonction en arguments nommés.
@mcp.resource("docs://documents", mime_type="application/json")
def list_docs() -> list[str]:
return list(docs.keys()) # le catalogue
@mcp.resource("docs://documents/{doc_id}", mime_type="text/plain")
def fetch_doc(doc_id: str) -> str: # une entrée, par paramètre d'URI
if doc_id not in docs:
raise ValueError(f"Doc with id {doc_id} not found")
return docs[doc_id]
Les resources comme catalogues de contenu. C'est le cadrage du guide lui-même et la raison pour laquelle les resources sont testées : exposer un catalogue — des résumés d'issues, une hiérarchie de documentation, un schéma de base de données — réduit les appels d'outils exploratoires.
Sans cela, un agent interrogé sur une table qu'il n'a jamais vue dépense trois
ou quatre appels à découvrir que la table existe, puis ses colonnes, puis ses
clés. Avec le schéma exposé en resource, l'application le lit une fois et le met
dans le prompt, et le premier appel que fait l'agent est celui qui fait le
travail. mime_type dit au client comment parser ce qui revient :
application/json pour des données structurées, text/plain pour du texte.
La division du travail est la chose à retenir : les tools servent le modèle, les resources servent votre application, les prompts servent vos utilisateurs.
Quand l'agent ignore votre outil MCP et utilise Grep
Vous branchez un serveur exposant un outil de recherche de code bien plus
capable que Grep — il comprend la structure, suit les imports, renvoie du
contexte. L'agent continue d'appeler Grep. Le serveur tourne, l'outil est
listé : ce n'est pas un défaut de configuration.
La cause est celle de 2.1, vue du côté de l'intégration. Les outils intégrés
arrivent avec des descriptions détaillées et éprouvées. Beaucoup d'outils MCP
portent une ligne générée à partir d'un nom de fonction. Face à "Search files"
d'un côté et à la description complète de Grep de l'autre, le modèle prend
celle qu'il comprend.
La compétence du guide est la réparation : enrichir la description de l'outil MCP pour qu'elle explique ses capacités et ses sorties en détail, afin qu'elle cesse de perdre la comparaison.
# Avant — perd contre Grep à tous les coups
Search files.
# Après — énonce la capacité, la sortie, et ce que l'outil intégré ne sait pas faire
Searches the indexed codebase semantically. Given a symbol or a natural-language
description, returns the definition site, every caller with its file and line,
and the import chain that connects them. Unlike a textual search, it resolves
re-exports and aliases, so a function reached through a wrapper module is still
found. Use it to trace how a symbol is used across the repository; use Grep for
a literal string you can spell exactly.
Quand un énoncé décrit un agent qui préfère un outil intégré à un outil MCP plus capable, la réponse est de réparer la description, qui est le levier le moins coûteux et celui qui traite la cause.
Ce n'est pas de retirer ou de désactiver l'outil intégré, ce qui ôte une capacité légitimement utile pour les recherches littérales. Ce n'est pas d'ajouter au system prompt une instruction disant à l'agent de préférer l'outil MCP, ce qui est le piège de sensibilité aux mots-clés de 2.1 érigé en politique. Et ce n'est pas de construire une couche de routage devant les outils, ce qui remplace un correctif de deux phrases par un composant à maintenir.
Serveur communautaire ou serveur sur mesure
La dernière compétence est une décision « acheter ou construire » assortie d'un défaut : utiliser un serveur MCP communautaire existant pour les intégrations standard — Jira, GitHub, Slack — et réserver un serveur sur mesure aux workflows spécifiques à votre équipe que personne d'autre n'a implémentés. N'importe qui peut écrire un serveur, et les fournisseurs de services en publient souvent d'officiels : l'intégration standard que vous vous apprêtez à écrire existe probablement, maintenue, avec ses schémas déjà éprouvés.
Écrire un serveur Jira sur mesure parce qu'il manque un champ à celui de la communauté. Vous avez adopté une charge de maintenance pour un delta. Le cadrage de l'examen lui-même est que les serveurs sur mesure sont pour ce qui est réellement spécifique à l'équipe.
Dupliquer une capacité intégrée dans un serveur MCP. Si Grep répond déjà
au besoin, un serveur qui fait du grep est une deuxième manière de faire une
seule chose. Il vous vaut en prime le problème de sélection de 2.3.
Teste-toi sur cette section
Q6 (Data Platform) — A personal server in the shared config
Scenario: a pull request adds a vault-notes entry to the repository's tracked .mcp.json. Its command field launches note-indexer from the author's home directory and points at ~/Notes/, a folder that exists on that laptop alone. The author's rationale: one file, one place to look.
Question: what should the reviewer ask for?
A) Merge it, then let each teammate delete the entry from their own checkout when the launch fails.
B) Move the entry to the author's user-scoped configuration.
C) Merge it after wrapping the launch command in a guard that exits quietly on machines without the binary, so nobody else sees an error.
D) Merge it and list .mcp.json in .gitignore, so the entry stops reaching anyone else.
Answer: the entry belongs to the user scope, not to the tracked file
Why: whatever the tracked file holds arrives with every clone, while ~/.claude.json stays on one machine. Both load at connection time, so the author keeps the tool without shipping a path nobody else has.
Why the others are wrong:
- Every checkout drifts from the branch, and the next pull brings the entry straight back.
- Silences the symptom while still shipping a machine-specific path to the whole team.
- The file is already tracked, so ignoring it now changes nothing — and it would hide the shared servers too.
5. 2.5 — Sélectionner et employer efficacement les outils intégrés (Read, Write, Edit, Bash, Grep, Glob)
Les six outils intégrés sont ceux dont un agent dispose déjà avant qu'aucun serveur MCP ne soit connecté, et le guide teste une chose à leur sujet : choisir le bon. Deux paires font l'essentiel des questions.
| Tâche | Outil | Exemple |
|---|---|---|
| Trouver des fichiers par nom ou motif de chemin | Glob | **/*.test.tsx, src/components/**/*.ts |
| Chercher dans le contenu des fichiers | Grep | Un nom de fonction, un message d'erreur, une ligne d'import |
| Charger un fichier entier | Read | Lire un module avant de le modifier |
| Créer un fichier, ou en réécrire un entièrement | Write | Un nouveau fichier ; un remplacement complet |
| Modifier une partie d'un fichier existant | Edit | Remplacer un extrait identifié par une correspondance de texte unique |
| Exécuter une commande shell | Bash | git, npm, les tests, un build |
Grep contre Glob est la première paire, et le piège est que les deux sont
décrits comme « cherchant ». Glob n'ouvre jamais un fichier : il fait
correspondre des chemins. Grep se moque du nom de fichier : il fait correspondre
du contenu. « Trouve tous les fichiers de test du parcours de commande », c'est
Glob. « Trouve tous les appelants de applyDiscount », c'est Grep.
Les deux s'appellent « recherche » ; un seul ouvre un fichier. Les deux
formes de distracteur sont cette confusion jouée dans un sens ou dans l'autre :
Glob proposé pour une recherche de contenu, et une correspondance de contenu
comme describe( proposée pour ce qui est en réalité un motif de nom. Lisez la
demande pour savoir ce qui est mis en correspondance, un chemin ou un octet,
avant de lire les noms d'outils.
Quand Edit échoue, Read + Write est le repli documenté
Edit fonctionne en faisant correspondre un texte qui doit être unique dans
le fichier. Quand l'ancre apparaît plus d'une fois, la modification est
refusée, et le refus est délibéré : l'outil ne peut pas savoir de quelle
occurrence vous parliez.
Le guide nomme le repli : Read le fichier en entier, amender le contenu, et
le Write en retour.
# runbooks/db-failover.yaml — la ligne « owner: unassigned » apparaît 23 fois.
Edit(old_string="owner: unassigned", new_string="owner: sre-oncall")
→ refusé : l'ancre n'est pas unique.
# Repli
Read("runbooks/db-failover.yaml") # le fichier entier, avec sa structure
→ amender la seule étape 14
Write("runbooks/db-failover.yaml", …) # le fichier amendé, écrit en entier
La raison pour laquelle cela marche là où Edit ne pouvait pas, c'est que le
contenu complet porte la position que l'ancre ne savait pas exprimer : l'étape 14
est identifiable dans son contexte, même si son texte est identique à celui de
vingt-deux autres.
Relancer Edit avec la même ancre. La correspondance est déterministe ; une
correspondance non unique ne devient pas unique à la deuxième tentative.
Remplacer toutes les occurrences pour passer outre l'échec. Cela change vingt-trois lignes quand une seule était dans le périmètre, et cela ressemble à un succès.
Construire la compréhension par incréments
La stratégie du guide pour un codebase inconnu est explicite, et sa valeur tient autant au contexte qu'à la justesse : commencer par Grep pour trouver les points d'entrée, puis Read pour suivre les imports et tracer les flux, plutôt que de lire tous les fichiers d'emblée.
1. Grep "createCheckoutSession" → la définition, et trois sites d'appel
2. Read src/checkout/session.ts → ce qu'elle fait, et ce qu'elle importe
3. Grep "from './session'" → qui la consomme
4. Read les consommateurs → comment elle est utilisée
5. Répéter jusqu'à ce que le flux soit complet
Tout lire d'abord remplit la fenêtre de contexte de fichiers qui se révèlent sans rapport, et les fichiers pertinents entrent alors en concurrence d'attention avec eux. Chaque Grep de la boucle ci-dessus est un filtre qui décide de ce qui mérite un Read.
Tracer une fonction à travers des modules d'enrobage
La dernière compétence est un échec précis de l'approche naïve, et elle vaut
d'être connue comme procédure. Un seul Grep sur un nom de fonction rate tous
les appels passant par un alias ou un ré-export.
# Rate les appelants indirects
Grep "analyzeDocument"
# Deux passes, comme le guide les décrit
1. Grep "export.*analyzeDocument" → trouver tous ses noms exportés
export { analyzeDocument }
export { analyzeDocument as analyze }
export { analyzeDocument as docAnalyze }
2. Grep chacun de ces noms → analyzeDocument, analyze, docAnalyze
→ tous les appelants sont maintenant trouvés
L'ordre est la substance : identifier d'abord tous les noms exportés, puis chercher chaque nom à travers le codebase. Chercher le nom d'origine seul répond à une question plus étroite que celle qui était posée.
Trois formulations, trois réponses. « Trouver des fichiers correspondant à un
motif de nom » → Glob. « Trouver où cette fonction est appelée » → Grep. « Edit
a échoué parce que le texte n'est pas unique » → Read puis Write.
Les distracteurs sont des voisins proches. Glob proposé pour une recherche de
contenu et Grep pour un motif de nom de fichier sont les deux moitiés de la même
confusion. Bash avec find ou grep atteint la réponse via une dépendance au
shell là où un outil intégré répond directement. Tout lire d'emblée est présenté
comme de la rigueur et c'est un épuisement du contexte. Et rendre la tâche à un
humain, alors que le repli documenté existe, n'achète rien.
Teste-toi sur cette section
Q7 (Site Reliability) — One step among twenty-three identical ones
Scenario: the agent must assign step 14 of runbooks/db-failover.yaml, whose steps list carries the line owner: unassigned 23 times. Edit refuses the change: the anchor is not unique.
Question: what is the correct fallback?
A) Report that the runbook cannot be changed safely and hand the step back to the on-call engineer.
B) Read the runbook in full, then Write back the amended version.
C) Enable replace_all on the same call.
D) Append a corrected copy of the step at the end of the file, leaving the stale one for a later cleanup.
Answer: load the whole file, then write it back
Why: with no unique anchor available, the documented fallback is a full read followed by a write of the amended content. What the anchor could not express, complete content carries.
Why the others are wrong:
- Hands a solvable task to a human, and the step stays unassigned meanwhile.
- Assigns all twenty-three steps at once, when twenty-two of them must stay untouched.
- Records two owners for the same step, so the runbook now contradicts itself.