Aller au contenu principal

Tutoriel : les racines en pratique

Ce tutoriel montre comment implémenter les roots dans une application de conversion vidéo.

Étape 1 : Définir les roots

Idéalement, c’est l’utilisateur qui dicte quels fichiers et dossiers le serveur MCP peut accéder.

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(
    extension: str = Field(description="Extension de fichier (ex: mp4)"),
    ctx: Context
) -> 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(
    filename: str = Field(description="Nom du fichier vidéo à convertir"),
    ctx: Context
) -> 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

Implémentez la vérification de sécurité pour tout accès aux fichiers :

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/"

Points clés

  • Les roots sont définies par l’utilisateur lors du démarrage de l’application
  • Le SDK ne les applique pas automatiquement — vous devez vérifier vous-même
  • Toujours vérifier is_path_in_roots() 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