Aller au contenu principal
Leçon 7 sur 14 · MCP — Sujets avancés

Tutoriel : les racines en pratique

Leçon 7 sur 14 Mis à jour le 5 novembre 2026 Avancé
Sur cette page
  1. Étape 1 : Définir les roots
  2. Étape 2 : Créer des objets Root
  3. Étape 3 : Callback de roots
  4. Étape 4 : Utiliser les roots dans les outils
  5. Étape 5 : Accéder aux roots
  6. Étape 6 : Autoriser l’accès
  7. Flux complet
  8. À vous de jouer
  9. Points clés à retenir

Mise à jour du 2 novembre 2026 — roots. La spécification MCP 2026-07-28 déprécie les Roots. La recommandation officielle est de transmettre les répertoires ou fichiers par les paramètres des outils, par des URI de ressources ou par la configuration du serveur. Ce que vous apprenez ici reste utile pour maintenir des serveurs existants. SDK Python v2 : le code de ce tutoriel suit la branche 1.x ; en v2, la forme recommandée est le résolveur ListRoots() reçu par Annotated[ListRootsResult, Resolve(fonction)]. Source : Sampling and roots (SDK Python v2).

Ce tutoriel montre comment implémenter les roots dans une application de conversion vidéo. À la fin, votre client déclarera les dossiers autorisés au lancement, et vos outils y chercheront les fichiers sans jamais en sortir. Avant d’entrer dans les étapes, rappelez-vous le point établi dans la leçon théorique, car il commande tout le code qui suit : le SDK ne fait rien appliquer. Déclarer des roots ne protège aucun fichier — c’est un canal d’information entre le client et le serveur, et la vérification d’accès reste entièrement à votre charge, à l’étape 6. Un serveur qui oublie cette vérification fonctionne parfaitement… en accédant à tout.

Étape 1 : Définir les roots

Idéalement, c’est l’utilisateur qui dicte quels fichiers et dossiers le serveur MCP peut atteindre — et ce choix de conception n’est pas anodin. Des chemins codés en dur dans le serveur obligeraient chaque utilisateur à adapter le code à sa machine ; des chemins passés au lancement font de la permission un acte explicite, visible dans la ligne de commande. C’est le même modèle mental que le montage de volumes : on donne accès à ce qu’on nomme, rien d’autre.

Dans cet exemple, le programme accepte des arguments CLI qui sont interprétés comme les chemins auxquels l’utilisateur veut accorder l’accès :

# L'utilisateur spécifie les dossiers accessibles au démarrage
uv run main.py /Users/alice/Videos /Users/alice/Desktop

Ces chemins sont passés au MCPClient lors de l’initialisation.

Étape 2 : Créer des objets Root

Convertissez les chemins en objets Root du SDK MCP :

from mcp.types import Root
from pathlib import Path

def create_roots(paths: list[str]) -> list[Root]:
    roots = []
    for path in paths:
        abs_path = Path(path).resolve()
        roots.append(Root(
            uri=f"file://{abs_path}",
            name=abs_path.name
        ))
    return roots

Étape 3 : Callback de roots

Le serveur peut demander la liste des roots via un callback. Définissez ce callback côté client :

async def roots_callback() -> list[Root]:
    # Retourne la liste des roots que l'utilisateur a approuvées
    return client_roots  # défini au démarrage de l'application

Passez-le lors de la création de la session :

async with ClientSession(
    read,
    write,
    roots_callback=roots_callback  # ← ici
) as session:
    await session.initialize()

Étape 4 : Utiliser les roots dans les outils

Côté serveur, vos outils peuvent récupérer les roots et les utiliser :

@mcp.tool()
async def list_accessible_files(
    ctx: Context,
    extension: str = Field(description="Extension de fichier (ex: mp4)")
) -> list[str]:
    # Récupérer les roots disponibles
    roots_result = await ctx.session.list_roots()
    
    files = []
    for root in roots_result.roots:
        root_path = Path(root.uri.replace("file://", ""))
        # Trouver tous les fichiers avec l'extension demandée
        for f in root_path.rglob(f"*.{extension}"):
            files.append(str(f))
    
    return files

Étape 5 : Accéder aux roots

Pour accéder aux roots dans un outil, utilisez ctx.session.list_roots() :

@mcp.tool()
async def convert_video(
    ctx: Context,
    filename: str = Field(description="Nom du fichier vidéo à convertir")
) -> str:
    roots_result = await ctx.session.list_roots()
    
    # Chercher le fichier dans les roots
    video_path = find_file_in_roots(filename, roots_result.roots)
    if not video_path:
        return f"Fichier '{filename}' introuvable dans les dossiers accessibles"
    
    # Convertir...
    return await do_convert(video_path)

Étape 6 : Autoriser l’accès

Voici l’étape qui fait la différence entre une application sûre et une application qui semble l’être. La vérification doit travailler sur des chemins résolus — c’est ce que fait le code ci-dessous — car un chemin relatif contenant .. peut désigner n’importe quel point du disque tout en paraissant anodin. Comparer des chaînes de caractères ne suffit pas ; on compare des chemins canoniques.

def find_file_in_roots(filename: str, roots) -> Path | None:
    for root in roots:
        root_path = Path(root.uri.replace("file://", ""))
        # Chercher récursivement
        matches = list(root_path.rglob(filename))
        if matches:
            return matches[0]
    return None

def is_path_in_roots(path: Path, roots) -> bool:
    for root in roots:
        root_path = Path(root.uri.replace("file://", "")).resolve()
        if path.resolve().is_relative_to(root_path):
            return True
    return False

Flux complet

Utilisateur: "Convertis biking.mp4 en MOV"
    ↓
Claude appelle list_accessible_files(extension="mp4")
    ↓
Serveur → list_roots() → roots_callback → ["/Users/alice/Videos", ...]
    ↓
Serveur parcourt les roots → trouve "/Users/alice/Videos/biking.mp4"
    ↓
Claude appelle convert_video(filename="biking.mp4")
    ↓
Serveur trouve le fichier dans les roots → convertit
    ↓
"biking.mov créé dans /Users/alice/Videos/"

À vous de jouer

  1. Lancez votre client avec deux dossiers en arguments et appelez list_accessible_files(extension="md") : la liste doit se limiter à ces dossiers.
  2. Appelez convert_video avec un nom de fichier situé hors des roots : le message doit dire « introuvable dans les dossiers accessibles », pas lever une exception brute.
  3. Écrivez un test unitaire de is_path_in_roots() avec un chemin contenant .. et un lien symbolique.

Sources officielles : Roots walkthrough (Claude Academy) · Roots (spécification 2026-07-28) · Sampling and roots (SDK Python v2)

Points clés à retenir

  • Les roots sont définies par l’utilisateur au démarrage de l’application, converties en objets Root(uri="file://…") et servies par un roots_callback sur la session.
  • Côté serveur, ctx.session.list_roots() renvoie la liste ; le SDK ne les applique pas automatiquement.
  • Toujours vérifier is_path_in_roots() sur des chemins résolus avant tout accès au système de fichiers.
  • Les roots rendent l’expérience fluide : les utilisateurs n’ont pas à taper des chemins complets.