WIKI
CCDV-F Claude Certified Developer

07 · Tools and MCPs : écrire, exécuter et choisir les extensions de Claude

34 min de lecture Mis à jour le 21 octobre 2026
Sur cette page

Version : 1.1 · Date : 2026-10-22 · Domaine 8 : Tools and MCPs (10,6 %)

En une phrase : un outil se juge sur trois surfaces (nom, description, schéma) et sur le harness qui l'exécute (dispatch, validation, approbation, timeout, is_error) ; une capacité partagée par plusieurs applications ou tenue par une autre équipe devient un serveur MCP ; et pour chaque exigence on choisit le bon mécanisme : built-in tool si la plateforme le fournit, custom tool pour faire, Agent Skill pour savoir faire, MCP pour partager.

Domaine 8 : quatre façons d'étendre une application Claude

Le cours voisin Claude Architect, chapitre 02 traite déjà la conception des descriptions, les erreurs MCP structurées, tool_choice, la configuration .mcp.json de Claude Code et les outils intégrés de Claude Code. Ici, on y renvoie, et on ajoute ce qui est propre au CCDV-F : le côté développeur de la boucle (le harness en Python), l'approbation des appels, l'écriture d'un serveur MCP et ses transports, le MCP connector de l'API, et le choix entre built-in tool, custom tool, Skill et MCP.

Ce que l'examen demande#

Texte officiel du Claude Certified Developer – Foundations Exam Guide v1.0 (juillet 2026) :

# Skill (guide officiel, en anglais) Poids Section
8.1 Tool Implementation : Tool implementation practices for Claude applications, including tool use and function calling, configuration for external system interaction, tool description writing, error handling, tool usage patterns (agentic harness dispatch, client-side vs. server-side tools, approval patterns), and tool set construction best practices. 4,4 % 1
8.2 MCP Server Development : MCP server development practices, including server authoring, deployment, integration with Claude applications, MCP resources, tools, and prompts, and communication patterns (stdio, sockets, client vs. server). 2,1 % 2
8.3 Agentic Customization : Tradeoffs among built-in Tools, custom Tools, Skills, and MCPs for selecting and applying the appropriate approach for a given use case. 4,1 % 3

Convention du cours : le produit évolue plus vite que le guide v1.0. Quand c'est le cas, un encadré donne d'abord la réponse d'examen (celle du guide), puis l'état actuel du produit (relevé le 22/10/2026 sur la doc officielle).


0. Ce que le cours Architect couvre déjà#

Lire ces sections avant ce chapitre ; elles ne sont pas répétées ici.

Sujet Où
Client tools / server tools, le cycle tool_use → tool_result, une définition d'outil Architect § 1
Écrire une description, renommer, scinder, consolider, conflit avec le system prompt Architect § 2
Erreurs MCP : erreur de protocole ou isError, catégories, isRetryable Architect § 3
Un petit jeu d'outils par rôle, tool_choice (auto, any, tool, none ; any et tool = 400 sur Fable 5.1, Mythos 5.1, Opus 5.5 et Sonnet 5.5, voir 02 § 3.4) Architect § 4
Architecture MCP, portées et .mcp.json, découverte dans l'Agent SDK, resources comme catalogue, serveur TypeScript Architect § 5
Outils intégrés de Claude Code (Read, Grep, Glob, Edit, Bash…) Architect § 6

1. Tool Implementation (skill 8.1)#

1.1 Ce que l'examen teste#

  • Reconnaître qui exécute un outil (votre code ou Anthropic) et ce que cela implique (réseau, identifiants, tool_result).
  • Écrire la boucle côté application : dispatch par nom, validation, approbation, exécution bornée, retour d'erreur.
  • Choisir le bon niveau d'approbation pour une opération, et savoir que c'est du code qui bloque, pas le prompt.
  • Construire un jeu d'outils lisible : peu d'outils bien délimités, noms préfixés, résultats à fort signal.

1.2 Les trois surfaces, en bref#

Claude ne voit d'un outil que son name, sa description et son input_schema. Une bonne description dit, en substance : ce que fait l'outil, quand l'utiliser, quand ne pas l'utiliser (et quel outil voisin prendre à la place), ce que veut dire chaque paramètre, et ce que l'outil renvoie. La distinction avec un outil voisin s'écrit dans les deux descriptions : si seul l'un des deux dit « pas pour X, utiliser Y », Claude peut encore choisir l'autre par défaut. Détails et exemples : Architect § 2.2.

1.3 Qui exécute : client tools, server tools, client toolsets#

Client tools ou server tools : qui exécute, avec quels droits

Inventaire des outils fournis par Anthropic au 22/10/2026 (page Tool reference) :

Famille type actuels Exécution Remarque
Web search web_search_20260318, _20260209, _20250305 Server Versions _20260209+ : filtrage dynamique par code execution
Web fetch web_fetch_20260318, _20260309, _20260209, _20250910 Server allowed_domains ou blocked_domains, jamais les deux
Code execution code_execution_20260521, _20260120, _20250825 Server _20260120+ : programmatic tool calling
Advisor advisor_20260301 Server Beta advisor-tool-2026-03-01
Tool search tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119 Server Charge à la demande les outils en defer_loading
MCP connector mcp_toolset Server Beta mcp-client-2025-11-20 (voir § 2.6)
Memory memory_20250818 Client Schéma Anthropic, stockage chez vous
Bash, text editor bash_20250124, text_editor_20250728 (_20250124 pour les anciens modèles) Client Votre code exécute la commande ou l'édition
Computer use, browser use computer_toolset_20260801, browser_toolset_20260801 Client (toolset) Une entrée déclare plusieurs outils membres

À retenir : « fourni par Anthropic » ne veut pas dire « exécuté par Anthropic ». bash, text_editor, memory et les toolsets ont un schéma défini par Anthropic, mais c'est votre code qui les exécute, avec vos droits. Un server tool, lui, tourne chez Anthropic et n'atteint que l'Internet public : il ne verra jamais votre base interne.

Ce qui change concrètement dans la boucle :

Client tool Server tool
Bloc de réponse tool_use (id toolu_…) server_tool_use (id srvtoolu_…) suivi de son *_tool_result
stop_reason tool_use : vous devez répondre Le tour continue ; pause_turn si la boucle serveur est longue
Vous renvoyez Un tool_result par tool_use Rien ; sur pause_turn, la réponse telle quelle avec les mêmes tools
Erreurs À vous de les formuler (is_error) Gérées par l'API (code d'erreur dans le bloc résultat)

Le tour mixte (piège classique) : si Claude appelle dans le même tour un server tool et un de vos outils, l'API n'exécute pas le server tool tout de suite. La réponse finit en tool_use, avec un server_tool_use sans résultat. Vous renvoyez un message user qui ne contient que les tool_result de vos outils, avec le même tableau tools ; l'API exécute alors le server tool et Claude continue.

sequenceDiagram
    participant App as "Votre harness"
    participant API as "Claude API"
    App->>API: "messages + tools (web_fetch, run_command)"
    API-->>App: "stop_reason tool_use : server_tool_use (sans résultat) + tool_use"
    Note over App: "exécute run_command uniquement"
    App->>API: "user : tool_result seuls, mêmes tools"
    API->>API: "exécute web_fetch"
    API-->>App: "web_fetch_tool_result + texte, stop_reason end_turn"

Erreurs 400 typiques : un texte ajouté après les tool_result (« … was found without a corresponding web_fetch_tool_result block »), un tool_result manquant ou placé après du texte (« tool_use ids were found without tool_result blocks immediately after »), ou le server tool retiré de tools à la reprise.

1.4 Le harness : du tool_use au tool_result#

Le harness : tout ce qui se passe entre tool_use et tool_result

Le harness dispatch du guide, c'est votre code entre la réponse de Claude et la requête suivante. Cinq étapes, dans l'ordre :

  1. Résoudre le nom vers un handler (un dictionnaire suffit) ; nom inconnu = tool_result en erreur, pas une exception.
  2. Revalider l'input : strict: true garantit le schéma, pas les règles métier (bornes, droits sur l'objet visé).
  3. Appliquer la politique d'approbation de l'opération (§ 1.6).
  4. Exécuter sous timeout et journaliser l'appel.
  5. Rendre un tool_result : contenu à fort signal en cas de succès, is_error: true avec un message utile sinon.

Exemple complet, Messages API en Python (clé dans ANTHROPIC_API_KEY) :

# ==== Boucle de tool use : dispatch, validation, approbation, timeout, erreurs exploitables par Claude ====
import concurrent.futures as cf
import json

import anthropic

client = anthropic.Anthropic()
MAX_ITERATIONS = 10   # plafond de la boucle, pauses serveur comprises
TIMEOUT_S = 20

TOOLS = [
    {"type": "web_fetch_20250910", "name": "web_fetch", "max_uses": 5},  # server tool : rien à exécuter ici
    {
        "name": "batch_get_state",
        "description": "Renvoie l'état de libération d'un lot de production (lecture seule). Utiliser pour toute question "
                       "sur un lot précis. Ne libère rien : pour libérer, utiliser batch_release.",
        "input_schema": {"type": "object", "properties": {"batch_id": {"type": "string", "description": "LOT-AAAA-NNNN"}},
                         "required": ["batch_id"], "additionalProperties": False},
        "strict": True,
    },
    {
        "name": "batch_release",
        "description": "Libère un lot contrôlé pour expédition (action irréversible). Uniquement après batch_get_state "
                       "et si tous les contrôles sont conformes.",
        "input_schema": {"type": "object", "properties": {"batch_id": {"type": "string"}, "reason": {"type": "string"}},
                         "required": ["batch_id", "reason"], "additionalProperties": False},
        "strict": True,
    },
]

# Nom -> (fonction, bande d'approbation) ; la bande se décide à la conception, par opération
HANDLERS = {
    "batch_get_state": (get_batch_state, "automatic"),
    "batch_release": (release_batch, "two-party"),
}
pool = cf.ThreadPoolExecutor(max_workers=4)


def tool_error(tool_use_id: str, message: str) -> dict:
    return {"type": "tool_result", "tool_use_id": tool_use_id, "is_error": True, "content": message}


def run_tool(block) -> dict:
    entry = HANDLERS.get(block.name)
    if entry is None:
        return tool_error(block.id, f"Outil inconnu : {block.name}. Outils disponibles : {', '.join(HANDLERS)}.")
    handler, band = entry
    try:
        validate_business_rules(block.name, block.input)
    except ValueError as exc:
        return tool_error(block.id, f"Entrée refusée : {exc}. Corriger l'argument puis réessayer.")
    if not approve(block.name, block.input, band):
        return tool_error(block.id, "Refusé par l'opérateur. Ne pas réessayer ; proposer une autre option à l'utilisateur.")
    try:
        # Le timeout libère la boucle mais pas le thread : borner aussi l'appel réseau du handler
        result = pool.submit(handler, **block.input).result(timeout=TIMEOUT_S)
    except cf.TimeoutError:
        return tool_error(block.id, f"Délai de {TIMEOUT_S} s dépassé. Réessayer une fois, puis signaler l'indisponibilité.")
    except Exception as exc:
        return tool_error(block.id, f"Échec interne ({type(exc).__name__}). Réessai inutile ; prévenir l'utilisateur.")
    return {"type": "tool_result", "tool_use_id": block.id, "content": json.dumps(result, ensure_ascii=False)}


def run(user_text: str):
    messages = [{"role": "user", "content": user_text}]
    for _ in range(MAX_ITERATIONS):
        response = client.messages.create(model="claude-opus-5-5", max_tokens=4096, tools=TOOLS, messages=messages)
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason == "pause_turn":
            continue  # boucle serveur en pause : renvoyer la réponse telle quelle, mêmes tools
        if response.stop_reason != "tool_use":
            return response
        # Tous les résultats du tour dans UN message user, et rien d'autre (tour mixte compris)
        messages.append({"role": "user", "content": [run_tool(b) for b in response.content if b.type == "tool_use"]})
    raise RuntimeError("Plafond d'itérations atteint")

Les fonctions get_batch_state, release_batch, validate_business_rules et approve sont les vôtres. Règles que le code applique :

Règle Pourquoi
Les tool_result arrivent dans le message qui suit immédiatement le tool_use, et en premier dans ce message Sinon 400 ; un texte éventuel vient après les résultats (jamais dans un tour mixte)
Un tool_result par tool_use, appels parallèles dans un seul message Plusieurs messages séparés apprennent à Claude à ne plus paralléliser
is_error: true avec quoi a échoué, si un réessai a un sens, à quoi ressemble une entrée valide Claude se corrige (il réessaie en général 2 à 3 fois sur une entrée invalide)
Jamais de liste vide pour dire « ça a échoué » Claude conclut « aucun résultat » et répond faux avec assurance
Le contenu tiers (page web, ticket, e-mail) reste dans le tool_result, jamais dans le system prompt Il doit rester une donnée, pas une instruction de confiance
Plafond d'itérations, même avec pause_turn Une boucle sans fin coûte cher et ne se voit pas

Moins de code : le Tool Runner des SDK (client.beta.messages.tool_runner() en Python) automatise cette boucle pour des fonctions décorées. Il ne remplace ni la validation métier ni la politique d'approbation : à vous de les mettre dans les fonctions. Les helpers MCP du SDK convertissent aussi les outils d'un serveur MCP pour ce runner (§ 2.6).

1.5 Fiabiliser les appels : strict, input_examples, propriétés d'outil#

Propriété Effet Disponible sur
strict: true Garantit que nom et input respectent le schéma (plus d'appel invalide à corriger) Tous, sauf mcp_toolset et les toolsets client
input_examples Exemples d'input pour les schémas ambigus (formats, champs liés) Outils définis par vous et client tools Anthropic ; pas les server tools
defer_loading: true L'outil n'est chargé que si le tool search le renvoie Tous ; préserve le cache de prompt
allowed_callers "direct" (Claude) et/ou "code_execution_20260120" (programmatic tool calling) Tous sauf mcp_toolset
cache_control Point de cache sur la définition Tous
eager_input_streaming Streaming fin de l'input Outils définis par vous

1.6 Approuver les appels#

Le guide parle d'approval patterns. Le schéma de référence, enseigné par le cursus tiers, classe chaque opération dans une bande décidée à la conception :

Bande Pour quoi Mise en œuvre
Automatic Lecture, calcul, action réversible et bornée Exécution directe, journalisée
Confirm Écriture réversible, coût modéré Une confirmation humaine avant exécution
Two-party Irréversible, financier, réglementaire, sortie de données Deux personnes distinctes (demandeur + approbateur)

Trois règles d'examen : en cas de doute, la bande la plus stricte ; l'approbation est un contrôle dans le code, avant l'exécution, jamais une phrase du prompt (« demande toujours avant de supprimer ») ; et trop de confirmations sur des actions anodines crée une fatigue d'approbation (les gens valident sans lire) : remonter en automatic ce qui est sans risque.

Nuance : les bandes automatic / confirm / two-party sont un patron de conception, pas un paramètre d'API. Côté MCP, la spécification recommande un humain capable de refuser tout appel, et précise que les annotations d'outil d'un serveur (indices comme readOnlyHint ou destructiveHint) doivent être traitées comme non fiables sauf serveur de confiance : elles ne remplacent pas votre politique.

Dans le Claude Agent SDK, l'approbation est intégrée. Chaque appel d'outil traverse six étapes, dans cet ordre :

Approuver un appel d'outil dans l'Agent SDK : six étapes

  • Un deny (hook ou règle) l'emporte sur tout, y compris bypassPermissions.
  • canUseTool n'est appelé que si rien avant ne s'est prononcé : un outil listé dans allowed_tools ou couvert par acceptEdits ne passe jamais par votre callback. Pour un contrôle sur chaque appel : un hook PreToolUse.
  • Un outil MCP dont le serveur pose _meta["anthropic/requiresUserInteraction"] va toujours au callback.
# ==== Approbation humaine des appels d'outil avec le Claude Agent SDK ====
from claude_agent_sdk import ClaudeAgentOptions, query
from claude_agent_sdk.types import (HookMatcher, PermissionResultAllow, PermissionResultDeny,
                                    ToolPermissionContext)


async def can_use_tool(tool_name: str, input_data: dict, context: ToolPermissionContext):
    if tool_name == "Bash" and "rm " in input_data.get("command", ""):
        return PermissionResultDeny(message="Suppression refusée : archiver les fichiers à la place.")
    if await ask_operator(tool_name, input_data):  # votre interface
        return PermissionResultAllow(updated_input=input_data)
    return PermissionResultDeny(message="Refusé par l'opérateur.")


async def dummy_hook(input_data, tool_use_id, context):
    return {"continue_": True}  # contournement documenté : garde le flux ouvert pour can_use_tool en Python


async def prompt_stream():
    yield {"type": "user", "message": {"role": "user", "content": "Nettoie le dossier /tmp/rapports"}}


options = ClaudeAgentOptions(can_use_tool=can_use_tool,
                             hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]})
# async for message in query(prompt=prompt_stream(), options=options): ...

En Python, can_use_tool exige un prompt en streaming input (générateur) et le hook factice ci-dessus. La réponse peut aussi modifier l'input (updated_input) : le modèle voit le résultat sans savoir que l'entrée a changé. Les hooks et la défense en profondeur sont au chapitre 06.

1.7 Construire le jeu d'outils#

Bonne pratique Exemple
Consolider les opérations que l'on enchaîne toujours schedule_event (trouve le créneau et réserve) plutôt que list_users + list_events + create_event
Préfixer par service ou ressource quand les outils se multiplient asana_projects_search, jira_issues_search
Renvoyer peu mais utile : identifiants lisibles, champs filtrés, pagination Un nom et un statut plutôt qu'un UUID et 40 champs bruts
Un petit jeu par rôle ou par agent Un agent de lecture n'a pas batch_release (Architect § 4.2)
Beaucoup d'outils : defer_loading + tool search Seuls les outils trouvés entrent dans le contexte
Beaucoup d'appels enchaînés : programmatic tool calling Claude écrit un script qui appelle les outils dans code execution ; seul le résultat final revient
Résultats documentaires : blocs search_result Citations propres vers la source

Quand Claude choisit le mauvais outil, corriger d'abord la description (et celle du voisin), puis renommer ou fusionner ; une couche de routage devant le modèle (« router ») n'est presque jamais la réponse attendue.


2. MCP Server Development (skill 8.2)#

2.1 Ce que l'examen teste#

  • Choisir la bonne primitive (tool, resource, prompt) pour une capacité.
  • Écrire un serveur minimal et le déployer avec le bon transport selon la topologie.
  • Savoir qui est client et qui est serveur, et comment le brancher à Claude Code, à l'Agent SDK ou à l'API.

2.2 Trois primitives, trois décideurs#

Un serveur MCP : trois primitives, trois décideurs, deux transports

Primitive Contrôlée par Méthodes Pour
Tool Le modèle (il décide de l'appeler) tools/list, tools/call Une action ou une requête paramétrée
Resource L'application (elle décide quoi charger) resources/list, resources/read, URI templates Une donnée passive : schéma, catalogue, fichier
Prompt L'utilisateur (il le choisit, souvent par une commande) prompts/list, prompts/get Une trame de travail réutilisable

Le client MCP vit dans l'application hôte (Claude Code, Claude Desktop, votre code) : un client par serveur. Le serveur expose les primitives ; il ne parle jamais au modèle directement. Architecture détaillée : Architect § 5.2.

2.3 Écrire un serveur en Python#

SDK officiel (uv add "mcp[cli]"). Le cours Claude Architecte montre la version TypeScript (§ 5.7).

# ==== Serveur MCP « batch-records » : un tool, une resource, un prompt (SDK Python officiel) ====
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError

mcp = MCPServer("batch-records")


@mcp.tool()
def batch_state(batch_id: str) -> dict:
    """Renvoie l'état de libération d'un lot (lecture seule). Utiliser pour toute question sur un lot précis."""
    record = load_batch(batch_id)  # votre accès aux données
    if record is None:
        # Erreur d'exécution : le modèle la voit (is_error) et peut corriger l'identifiant
        raise ToolError(f"Lot {batch_id} introuvable. Format attendu : LOT-AAAA-NNNN.")
    return record


@mcp.resource("records://schema/{record_type}")
def record_schema(record_type: str) -> str:
    """Schéma d'un type d'enregistrement, chargé par l'application si besoin."""
    return load_schema(record_type)


@mcp.prompt()
def batch_inspection(batch_id: str) -> str:
    """Trame d'inspection d'un lot, choisie par l'utilisateur."""
    return f"Inspecte le lot {batch_id} : état, contrôles non conformes, décision recommandée avec justification."


if __name__ == "__main__":
    mcp.run(transport="stdio")
  • Le nom de la fonction devient le nom de l'outil, la docstring sa description, les annotations de type son schéma : la docstring mérite le même soin qu'une description d'outil de l'API.
  • ToolError : erreur d'exécution, renvoyée au modèle avec isError. MCPError (from mcp import MCPError) : erreur de protocole JSON-RPC, vue par l'hôte. Voir Architect § 3.2.
  • Un serveur valide toutes ses entrées, contrôle les accès et limite le débit (exigences de la spécification) : il ne fait pas confiance au client.

2.4 Les transports : stdio, Streamable HTTP, sockets#

Transport Fonctionnement Quand
stdio Le client lance le serveur en sous-processus ; JSON-RPC délimité par des retours à la ligne sur stdin / stdout Outil local, un seul utilisateur, accès aux fichiers de la machine
Streamable HTTP Un POST par message vers un endpoint unique (/mcp) ; réponse JSON ou flux SSE propre à la requête Service partagé, plusieurs clients, authentification (OAuth)
Custom (sockets Unix, TCP) Doit garder le format JSON-RPC ; devrait réutiliser le framing stdio Cas particuliers, interopérabilité à documenter

Réponse attendue à l'examen : le guide cite « stdio, sockets, client vs. server ». Retenir : stdio pour un serveur local lancé par le client, un transport réseau pour un serveur distant partagé.

État actuel (spécification 2026-07-28) : deux transports standard, stdio et Streamable HTTP. Les sockets (Unix ou TCP) sont des transports personnalisés qui reprennent le framing stdio. L'ancien transport HTTP+SSE est déprécié (le MCP connector de l'API l'accepte encore).

Piège stdio à connaître par cœur : stdout est le canal du protocole. Un print() de débogage corrompt les messages ; journaliser sur stderr.

Lancer le même serveur en HTTP : transport streamable-http, par exemple en CLI uv run mcp run server.py --transport streamable-http (README du SDK Python) ; les clients s'y connectent sur http://localhost:8000/mcp. L'exposer au-delà de localhost exige une authentification et la configuration de sécurité du transport (validation de l'en-tête Origin, TLS).

État actuel : MCP devient sans état (spécification 2026-07-28)

  • plus de session de protocole ni d'en-tête Mcp-Session-Id ; plus de poignée de main initialize : chaque requête porte sa version et ses capacités dans _meta ;
  • server/discover devient obligatoire côté serveur ; subscriptions/listen remplace l'abonnement aux changements ;
  • un état entre appels passe par un identifiant explicite renvoyé par un outil et repassé en argument (un panier, une transaction), avec contrôle d'autorisation à chaque appel ;
  • ping et logging/setLevel supprimés, reprise des flux SSE supprimée ; les tasks passent en extension ;
  • Roots, Sampling et Logging dépréciés ; ttlMs et cacheScope requis sur les listes ; ordre des outils déterministe.

Pour l'examen, ces détails comptent peu ; ce qui compte : primitives, transports, qui est client, qui est serveur.

2.5 Déployer : un serveur distant est un service#

Un serveur Streamable HTTP partagé a un propriétaire, une disponibilité, des versions et une authentification. Conséquences pratiques : versionner les outils (ne pas changer le sens d'un paramètre sans nouveau nom), donner des descriptions stables (le cache de prompt et les clients en dépendent), ne jamais mettre de secret dans un paramètre mirroré en en-tête HTTP (x-mcp-header), et tracer les appels pour l'audit.

2.6 Intégrer un serveur à une application Claude#

Brancher un serveur MCP : MCP connector de l'API ou client MCP dans votre code

Hôte Comment Référence
Claude Code claude mcp add ou .mcp.json (portées local, project, user) Architect § 5.3
Claude Agent SDK mcp_servers dans ClaudeAgentOptions ; outils nommés mcp__{serveur}__{outil} Architect § 5.4
Claude Desktop claude_desktop_config.json, clé mcpServers (commande et arguments du serveur stdio) Doc MCP Build a server
Messages API, serveur distant MCP connector : mcp_servers + mcp_toolset Ci-dessous
Messages API, serveur local ou prompts / resources Votre client MCP + helpers du SDK (pip install "anthropic[mcp]") Ci-dessous

Le MCP connector fait de l'API le client MCP : pas de code client chez vous.

# ==== MCP connector : l'API appelle elle-même le serveur MCP distant ====
import os

import anthropic

client = anthropic.Anthropic()
response = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=2048,
    messages=[{"role": "user", "content": "Quel est l'état du lot LOT-2026-0412 ?"}],
    mcp_servers=[{
        "type": "url",
        "url": "https://mcp.example.com/mcp",                   # HTTPS public, Streamable HTTP ou SSE
        "name": "batch-records",
        "authorization_token": os.environ["BATCH_MCP_TOKEN"],   # jeton OAuth, jamais en dur
    }],
    tools=[{
        "type": "mcp_toolset",
        "mcp_server_name": "batch-records",
        "default_config": {"enabled": False},                   # liste blanche : tout fermé…
        "configs": {"batch_state": {"enabled": True}},          # …sauf la lecture
    }],
    betas=["mcp-client-2025-11-20"],
)

La réponse contient des blocs mcp_tool_use (id mcptoolu_…, server_name) et mcp_tool_result. Priorité de configuration : configs > default_config > valeurs par défaut (enabled: true, defer_loading: false).

Piège d'examen : le MCP connector ne transporte que les tools (ni resources ni prompts) et n'atteint que des serveurs publics en HTTP : un serveur stdio local ne se branche pas directement. Pour ça, l'application garde son propre client MCP et convertit les outils avec les helpers du SDK (async_mcp_tool, mcp_message, mcp_resource_to_content).

État actuel : le MCP connector est beta (Claude API, Claude Platform on AWS, Microsoft Foundry), non éligible ZDR. L'ancien en-tête mcp-client-2025-04-04 est déprécié. Le nouvel en-tête beta mcp-client-2026-09-15 permet de figer la liste d'outils d'un serveur (champ tools, blocs mcp_tool_listing) pour qu'elle ne change pas en cours de conversation.


3. Agentic Customization (skill 8.3)#

3.1 Ce que l'examen teste#

Une situation, une exigence, et le choix entre built-in tool, custom tool, Agent Skill et serveur MCP. Les questions mélangent souvent plusieurs exigences : chacune a son mécanisme, et une réponse qui met le même partout est presque toujours fausse.

3.2 Les quatre mécanismes#

Mécanisme Ce que c'est Force Limite
Built-in tool Outil fourni par la plateforme (web search, web fetch, code execution ; Read, Bash… dans Claude Code) Zéro code, maintenu par Anthropic Ne connaît pas vos systèmes privés
Custom tool Votre fonction, décrite dans tools et exécutée par votre harness Accès à vos systèmes, vos identifiants Lié à une application ; à maintenir
Agent Skill Dossier avec SKILL.md (+ fichiers, scripts) chargé à la demande Savoir-faire procédural sans coût de contexte permanent N'ouvre aucun accès par lui-même
Serveur MCP Capacité exposée par protocole standard à plusieurs hôtes Réutilisable, propriétaire distinct, versionné Un service à exploiter

3.3 Agent Skills#

Agent Skills : trois niveaux chargés au moment où ils servent

Structure : un dossier avec un SKILL.md dont le frontmatter YAML porte deux champs obligatoires.

---
name: batch-release-report
description: Rédige le rapport de libération d'un lot selon les conventions qualité (sections, formulations, seuils).
  Utiliser quand l'utilisateur demande un rapport de libération, une synthèse de lot ou un dossier qualité.
---

# Rapport de libération

1. Récupérer l'état du lot avec l'outil batch_state.
2. Suivre le plan de `TEMPLATE.md` ; seuils dans `SEUILS.md`.
3. Vérifier le rapport avec `python scripts/check_report.py rapport.md`.
Champ Contraintes
name 64 caractères max ; minuscules, chiffres et tirets ; pas de balise XML ; ni « anthropic » ni « claude »
description Non vide, 1 024 caractères max, pas de balise XML ; dit ce que fait le Skill et quand l'utiliser

Progressive disclosure : niveau 1, les métadonnées (~100 tokens par Skill) sont toujours chargées ; niveau 2, le corps de SKILL.md (moins de 5k tokens conseillés) entre dans le contexte quand la demande correspond à la description ; niveau 3, les fichiers ne coûtent rien tant qu'ils ne sont pas lus, et d'un script seule la sortie entre dans le contexte. On peut donc installer beaucoup de Skills sans payer leur contenu.

Sur l'API, un Skill tourne dans le conteneur de code execution :

# ==== Agent Skills sur la Messages API : un Skill Anthropic et un Skill maison ====
import anthropic
from anthropic.lib import files_from_dir

client = anthropic.Anthropic()
skill = client.skills.create(files=files_from_dir("batch-release-report"))  # partagé dans tout le workspace

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    container={"skills": [
        {"type": "anthropic", "skill_id": "docx", "version": "latest"},
        {"type": "custom", "skill_id": skill.id, "version": "latest"},
    ]},
    tools=[{"type": "code_execution_20250825", "name": "code_execution"}],  # prérequis des Skills
    messages=[{"role": "user", "content": "Rapport de libération du lot LOT-2026-0412 en Word"}],
)
Point Valeur (22/10/2026)
Skills Anthropic pptx, xlsx, docx, pdf
Par requête 20 Skills au plus
Upload d'un Skill moins de 30 Mo décompressé ; une nouvelle version est un instantané complet ; version = latest ou une version précise
Réseau sur l'API aucun, et pas d'installation de paquets à l'exécution
Claude Code ~/.claude/skills/ (personnel) ou .claude/skills/ (projet) ; réseau complet
claude.ai zip dans Settings > Features, par utilisateur
Synchronisation entre surfaces aucune : téléverser sur chaque surface
Sécurité Skills de confiance seulement (les vôtres, ceux d'Anthropic) ; auditer un Skill tiers comme un logiciel
ZDR non couvert

3.4 Choisir : quatre questions, dans l'ordre#

Built-in tool, custom tool, Skill ou MCP : une exigence, un mécanisme

flowchart TD
    A["Une exigence"] --> B{"La plateforme le fournit-elle déjà ?"}
    B -- "oui" --> C["Built-in tool"]
    B -- "non" --> D{"Faire quelque chose ou savoir comment faire ?"}
    D -- "savoir" --> E["Agent Skill"]
    D -- "faire" --> F{"Plusieurs consommateurs ou un autre propriétaire ?"}
    F -- "oui" --> G["Serveur MCP"]
    F -- "non" --> H["Custom tool"]
  1. La plateforme le fournit-elle ? Lire une page publique : web_fetch. Calculer sur un fichier : code execution. Ne pas réécrire ce qui existe.
  2. Faire ou savoir ? Agir sur un système (lire une base, créer un ticket) demande un outil. Appliquer des conventions, une procédure, un format demande un Skill, surtout si c'est occasionnel (pas à chaque conversation).
  3. Qui d'autre l'utilise, qui le maintient ? Une seule application : custom tool. Plusieurs applications, plusieurs équipes, ou une équipe propriétaire distincte : serveur MCP. Le critère est la propriété et la maintenance, pas la technique.
  4. Les deux moitiés d'un besoin : souvent un outil (ou un MCP) donne l'accès, et un Skill donne la méthode pour s'en servir. Ce n'est pas l'un ou l'autre.

Erreurs récurrentes dans les réponses proposées :

Réponse tentante Pourquoi elle est fausse
Coller les données dans le prompt Instantané périmé, contexte gaspillé, pas d'accès en direct
Recopier la procédure dans chaque system prompt Coût de contexte permanent, versions divergentes : c'est le rôle d'un Skill
Un built-in tool pour atteindre un service interne Les server tools ne voient que l'Internet public
Fine-tuner le modèle pour lui « donner accès » Le fine-tuning n'ouvre aucun accès à un système
Un serveur MCP pour une fonction utilisée par une seule appli Un service de plus à exploiter, sans bénéfice de partage

4. Labs conseillés#

Un compte Claude Console avec une clé dans ANTHROPIC_API_KEY (jamais dans le code). Petit modèle, quelques centimes par lab.

  1. Harness complet (40 min)
    • Reprendre le code de la § 1.4 avec deux fausses fonctions (dictionnaire en mémoire).
    • Provoquer chaque branche : nom inconnu, input hors bornes, refus d'approbation, timeout (un time.sleep).
    • Attendu : Claude lit chaque is_error et change d'approche ; aucune exception ne remonte.
  2. Tour mixte (20 min)
    • Déclarer web_fetch et un outil local ; demander une tâche qui utilise les deux.
    • Ajouter volontairement un texte après les tool_result, puis retirer web_fetch de tools à la reprise.
    • Attendu : les deux erreurs 400 de la § 1.3, puis un tour qui passe.
  3. Serveur MCP local (30 min)
    • Écrire le serveur de la § 2.3, l'ajouter à Claude Code (claude mcp add), l'interroger.
    • Ajouter un print() dans un outil.
    • Attendu : le serveur stdio casse ; le remplacer par un log sur stderr répare.
  4. MCP connector (20 min)
    • Pointer le connector vers un serveur MCP public de test, avec liste blanche d'un seul outil.
    • Attendu : des blocs mcp_tool_use / mcp_tool_result ; l'outil non autorisé n'est jamais appelé.
  5. Skill maison (30 min)
    • Créer un Skill de rapport dans .claude/skills/ (Claude Code), avec une description qui dit quoi et quand.
    • Tester une demande qui devrait le déclencher et une qui ne devrait pas ; rendre la description trop vague et recommencer.
    • Attendu : le déclenchement dépend de la description seule.

Nettoyage : supprimer les clés de test et les Skills téléversés par l'API (client.skills) s'il y en a.


5. Pièges d'examen#

Situation dans la question Réponse attendue Pourquoi
Un server tool doit lire la base interne Impossible : custom tool ou MCP Les server tools tournent chez Anthropic, Internet public seulement
« bash est fourni par Anthropic, donc exécuté chez Anthropic » Faux : client tool Schéma Anthropic, exécution par votre code
Après un server_tool_use, renvoyer un tool_result Non L'API exécute et rend le résultat elle-même
stop_reason: pause_turn Renvoyer la réponse telle quelle, mêmes tools, avec un plafond Boucle serveur en pause, pas une erreur
Tour mixte : ajouter une consigne après les tool_result Non : message user avec seulement les tool_result Sinon 400 ; la consigne vient au tour suivant
Un outil qui échoue renvoie [] Non : is_error: true et un message utile Une liste vide se lit « aucun résultat »
Résultats de trois appels parallèles dans trois messages Un seul message user, trois tool_result Sinon Claude cesse de paralléliser
Texte avant les tool_result dans le message user Erreur 400 Les résultats viennent en premier
Contenu d'une page web injecté dans le system prompt Le laisser dans le tool_result Donnée non fiable ≠ instruction
« Demande confirmation avant de supprimer » dans le prompt Insuffisant : contrôle dans le code (bande, canUseTool, hook) Le prompt demande, le code impose
Bande d'approbation incertaine La plus stricte (two-party) Le coût d'une erreur irréversible domine
Annotation readOnlyHint d'un serveur tiers Indice non fiable La spec impose de s'en méfier
canUseTool jamais appelé pour Write L'outil est déjà approuvé (allow rule, acceptEdits) Seul un hook PreToolUse voit tout
bypassPermissions contourne un deny Faux Deny rules et hooks passent avant
Claude confond deux outils proches Réécrire les deux descriptions (quand / quand ne pas) Avant tout router ou fine-tuning
60 outils, contexte saturé defer_loading + tool search, ou consolider Moins d'outils visibles, mieux choisis
Serveur stdio qui « répond n'importe quoi » Un print() sur stdout stdout = canal du protocole
MCP connector vers un serveur stdio local Impossible : serveur HTTP public ou client MCP local Limite documentée du connector
MCP connector pour exposer des prompts MCP Impossible : tools seulement Prompts et resources via un client MCP à vous
Action vs donnée passive vs trame choisie par l'utilisateur Tool / resource / prompt Le décideur diffère : modèle, application, utilisateur
Procédure de rédaction utile une fois sur cinq Agent Skill Chargé seulement quand il sert
Une capacité interne utilisée par trois applications Serveur MCP Propriété et maintenance uniques
Skill téléversé sur l'API, absent dans claude.ai Normal Aucune synchronisation entre surfaces
Un Skill sur l'API doit appeler une API externe Impossible Pas de réseau dans le conteneur API
name: claude-helper pour un Skill Refusé « claude » et « anthropic » sont réservés

6. Scénarios corrigés (QCM originaux)#

1. Un assistant interne doit consulter le stock dans l'ERP de l'entreprise, qui n'est pas exposé sur Internet. Une seule application en a besoin. Que faire ? A. Activer web_fetch avec allowed_domains sur le nom de l'ERP B. Un custom tool exécuté par l'application, avec ses propres identifiants C. Un Agent Skill qui décrit l'API de l'ERP D. Coller l'export du stock dans le system prompt chaque matin

B. Il faut faire (lire l'ERP) depuis le réseau privé, pour une seule application. A : un server tool n'atteint pas un hôte privé. C : un Skill donne une méthode, pas un accès. D : instantané périmé et contexte gaspillé.

2. La réponse de l'API contient un server_tool_use (web_fetch) sans résultat et un tool_use (run_command), avec stop_reason: "tool_use". Que renvoyer ? A. La réponse telle quelle, comme pour pause_turn B. Un message user avec le tool_result de run_command puis une phrase « continue » C. Un message user avec seulement le tool_result de run_command, et le même tableau tools D. Deux tool_result, un pour chaque bloc

C. L'API exécutera web_fetch à réception. B : le texte après les résultats ferme le tour et provoque un 400. A : le tool_use attend votre résultat. D : on ne fournit pas de résultat pour un server tool.

3. Un outil search_orders renvoie [] quand la base est indisponible. Claude répond alors « vous n'avez aucune commande ». Correction ? A. Ajouter au system prompt « si la liste est vide, la base est peut-être indisponible » B. Renvoyer un tool_result avec is_error: true et « Base des commandes indisponible, réessayer dans une minute » C. Passer strict: true sur l'outil D. Lever une exception dans le harness pour arrêter la boucle

B. L'échec doit se distinguer d'un résultat vide. A reste ambigu. C valide l'input, pas le résultat. D prive Claude de l'information et casse la conversation.

4. Un agent peut rembourser des clients via issue_refund. Les remboursements sont irréversibles et la politique impose la validation d'un responsable. Quelle mise en œuvre ? A. Une consigne dans le system prompt : « demande toujours l'accord d'un responsable » B. issue_refund en bande two-party : le harness bloque l'exécution tant qu'un approbateur distinct n'a pas validé C. tool_choice: {"type": "none"} sur toute la conversation D. Renommer l'outil en issue_refund_carefully

B. Contrôle dans le code, avant exécution, à deux personnes. A est une demande, pas une garantie. C supprime aussi les outils utiles. D ne contrôle rien.

5. Avec le Claude Agent SDK, une équipe veut journaliser et pouvoir refuser chaque appel à Write. Elle a mis Write dans allowed_tools et un callback can_use_tool. Le callback ne se déclenche jamais. Pourquoi ? A. can_use_tool ne fonctionne qu'en TypeScript B. Une allow rule approuve l'appel avant l'étape canUseTool ; il faut un hook PreToolUse C. Il faut passer en bypassPermissions D. Write n'est pas un outil réel

B. L'ordre est hooks, deny, ask, mode, allow, puis canUseTool. Un outil déjà approuvé n'atteint pas le callback.

6. Une équipe data maintient une API de référentiel produits. Trois applications Claude (support, ventes, achats) doivent la consulter, et l'équipe data veut garder la main sur les évolutions. Quel mécanisme ? A. Un custom tool copié dans chaque application B. Un serveur MCP tenu par l'équipe data, branché aux trois applications C. Un Agent Skill par application D. web_search limité au domaine du référentiel

B. Plusieurs consommateurs et un propriétaire distinct : MCP. A multiplie les copies divergentes. C n'ouvre pas d'accès. D : moteur de recherche public, sans rapport avec une API interne.

7. Un serveur MCP doit fournir (1) une action « créer un ticket », (2) le catalogue des catégories que l'application charge en contexte, (3) une trame « revue d'incident » que l'utilisateur lance par une commande. Quelles primitives ? A. Trois tools B. Tool, resource, prompt C. Resource, tool, prompt D. Prompt, resource, tool

B. Action décidée par le modèle : tool. Donnée chargée par l'application : resource. Trame choisie par l'utilisateur : prompt.

8. Un développeur veut utiliser son serveur MCP stdio local depuis une application Messages API, avec ses prompts MCP. Que recommander ? A. Le MCP connector avec "url": "stdio://localhost" B. Un client MCP dans l'application et les helpers du SDK pour convertir outils et prompts C. Héberger le serveur en HTTP public et utiliser le MCP connector pour les prompts D. Impossible avec l'API

B. Le connector n'accepte que des serveurs HTTP publics, et seulement leurs tools. Le client local garde stdio, prompts et resources.

9. Les rédacteurs d'un cabinet appliquent une charte de rapport (structure, formulations, seuils) dans environ un dossier sur cinq. Aujourd'hui la charte de 6 000 mots est dans le system prompt de toutes les conversations. Amélioration ? A. Un serveur MCP qui renvoie la charte B. Un Agent Skill avec la charte en fichiers de référence, déclenché par sa description C. Fine-tuner le modèle sur des rapports conformes D. Réduire la charte à 500 mots dans le system prompt

B. Savoir-faire procédural et occasionnel : Skill, chargé seulement quand il sert. A ajoute un service sans besoin de partage. C est lourd et ne se met pas à jour. D perd l'information.

10. Un serveur MCP stdio fonctionne dans un terminal, mais Claude Code le marque en échec dès le premier appel d'outil. Le code contient print(f"debug: {batch_id}"). Cause ? A. stdio ne supporte pas Python B. Le print écrit sur stdout, le canal du protocole ; journaliser sur stderr C. Il manque ToolError D. Il faut passer en Streamable HTTP

B. Toute écriture hors protocole sur stdout corrompt les messages JSON-RPC.


7. Pour aller plus loin (hors examen ou état actuel)#

  • Programmatic tool calling : avec code_execution_20260120+, Claude écrit un script qui appelle vos outils (allowed_callers) ; les résultats intermédiaires ne passent pas par le contexte.
  • Advisor tool (beta) : un modèle conseiller consulté en cours de tour.
  • Multi round-trip requests (MCP 2026-07-28) : un serveur renvoie resultType: "input_required" pour demander une information (élicitation), et le client rejoue la requête avec la réponse.
  • Skills en entreprise : gouvernance, revue et analyse de contenu des Skills (page Skills for enterprise).
  • Lecture de fond : Writing effective tools for agents et Equipping agents for the real world with Agent Skills (Anthropic Engineering).

8. Fiche à retenir#

  • Trois surfaces : name, description (quoi, quand, quand ne pas, paramètres, retour), input_schema.
  • Fourni ≠ exécuté par Anthropic : bash, text_editor, memory, toolsets = client ; web search / fetch, code execution, tool search, mcp_toolset = server.
  • Server tool : srvtoolu_, pas de tool_result ; pause_turn = renvoyer tel quel, mêmes tools, plafonné.
  • Tour mixte : stop_reason: tool_use, répondre avec seulement les tool_result, même tableau tools.
  • tool_result juste après, en premier, un par tool_use, tous dans un message ; échec = is_error: true, jamais [].
  • Harness : résoudre → revalider → approuver → exécuter sous timeout → tool_result ; plafond d'itérations.
  • Bandes automatic / confirm / two-party, la plus stricte en cas de doute ; contrôle dans le code, pas dans le prompt.
  • Agent SDK : hooks → deny → ask → mode → allow → canUseTool ; seul PreToolUse voit tous les appels.
  • MCP : tool (modèle), resource (application), prompt (utilisateur) ; un client par serveur dans l'hôte.
  • stdio local, stdout réservé (logs sur stderr) ; Streamable HTTP distant ; sockets = transport custom au framing stdio.
  • MCP connector : beta mcp-client-2025-11-20, mcp_servers + mcp_toolset, tools seulement, serveur HTTP public.
  • Skill : SKILL.md, name ≤ 64 (sans « claude » ni « anthropic »), description ≤ 1 024 = quoi + quand ; trois niveaux.
  • Skills API : code execution + container.skills, 20 max, pas de réseau ; aucune synchronisation entre surfaces.
  • Choisir : fourni ? → built-in ; savoir → Skill ; faire pour une appli → custom tool ; partagé ou autre propriétaire → MCP.

Références#

Lues le 22/10/2026 :

Sur cette page
Échap

Tape au moins 2 lettres. Plusieurs mots : tous doivent apparaître.