Aller au contenu

Échantillonnage et racines

Traduction automatique

Cette page a été traduite automatiquement à partir de la documentation en anglais, et la page en anglais fait foi. Si quelque chose vous semble incorrect, la page Traductions explique comment le signaler.

Un gestionnaire (handler) peut demander deux choses de plus au client connecté : une complétion produite par le modèle du client lui-même — l’échantillonnage (sampling) — et les dossiers de l’espace de travail du client — les racines (roots).

Les deux fonctionnent toujours, sur toutes les versions du protocole que le SDK parle. Mais lisez l’avertissement avant de concevoir quoi que ce soit autour d’elles :

Rendus obsolètes par la spécification 2026-07-28

L’échantillonnage et les racines sont obsolètes depuis la version 2026-07-28 (SEP-2577). Ils restent pleinement fonctionnels et demeurent dans la spécification pendant au moins douze mois avant de pouvoir être supprimés, mais les nouvelles implémentations ne devraient pas s’appuyer dessus. Les migrations suggérées : intégrez-vous directement à l’API de votre fournisseur de LLM au lieu de l’échantillonnage, et transmettez les répertoires via des paramètres d’outil, des URI de ressource ou la configuration du serveur au lieu des racines. La liste complète pour le SDK se trouve dans Fonctionnalités obsolètes.

Échantillonnage : emprunter le modèle du client

Un résolveur renvoie Sample(...) et l’outil reçoit la complétion, par le même mécanisme de dépendances qui exécute Elicit dans Dépendances :

server.py
from typing import Annotated

from mcp.server import MCPServer
from mcp.server.mcpserver import Resolve, Sample
from mcp.types import CreateMessageResult, SamplingMessage, TextContent

mcp = MCPServer("Bookshop")


def draft_blurb(title: str) -> Sample:
    prompt = f"Write a one-sentence blurb for the book {title!r}."
    return Sample(
        [SamplingMessage(role="user", content=TextContent(type="text", text=prompt))],
        max_tokens=60,
    )


@mcp.tool()
async def blurb(title: str, draft: Annotated[CreateMessageResult, Resolve(draft_blurb)]) -> str:
    """Draft a blurb for a book."""
    return draft.content.text if draft.content.type == "text" else "No blurb."
  • Sample(messages, max_tokens=...) reprend les paramètres de sampling/createMessage. La valeur injectée est le CreateMessageResult du client ; passez tools ou tool_choice et elle devient un CreateMessageResultWithTools.
  • Le client doit avoir déclaré la capacité sampling (sampling.tools si vous passez tools ou tool_choice). S’il ne l’a pas fait, l’appel échoue avec une erreur de protocole -32021 au lieu d’envoyer une requête que le client ne peut pas traiter. Une session antérieure à 2026 sans canal de retour (back-channel) échoue avec son erreur habituelle d’absence de canal de retour, puisqu’il n’y a rien sur quoi envoyer.
  • En version 2026-07-28, la requête est acheminée dans le flux à plusieurs allers-retours (multi-round-trip) (Requêtes à plusieurs allers-retours) ; en version 2025-11-25, c’est une requête autonome adressée au client. Le code est le même dans les deux cas, mais gardez à l’esprit la règle des requêtes à plusieurs allers-retours : la requête doit être rendue à l’identique d’une tentative à l’autre, construisez-la donc uniquement à partir des arguments de l’outil et d’autres données stables.
  • Ne touchez pas à include_context : les valeurs autres que "none" sont elles-mêmes obsolètes (SEP-2596) et exigent une capacité que presque aucun client ne déclare.

Racines : où cela doit-il aller ?

Les racines sont les dossiers sur lesquels le client indique que le serveur peut opérer. Ce sont des indications à titre informatif, pas un mécanisme de contrôle d’accès. Un résolveur renvoie ListRoots() :

server.py
from typing import Annotated

from mcp.server import MCPServer
from mcp.server.mcpserver import ListRoots, Resolve
from mcp.types import ListRootsResult

mcp = MCPServer("Bookshop")


def workspace_roots() -> ListRoots:
    return ListRoots()


@mcp.tool()
async def catalog_folder(roots: Annotated[ListRootsResult, Resolve(workspace_roots)]) -> str:
    """Pick the folder the catalog export should go to."""
    if not roots.roots:
        return "No workspace folders shared."
    return str(roots.roots[0].uri)
  • Le ListRootsResult injecté contient une liste de Root : un URI file:// et un nom d’affichage facultatif.
  • Le garde-fou est le même que pour l’échantillonnage : sans capacité roots déclarée, l’appel échoue avec -32021 au lieu d’envoyer la requête.

De l’autre côté de la liaison, le client répond aux deux requêtes avec les fonctions de rappel (callbacks) dont il dispose déjà : sampling_callback et list_roots_callback, décrites dans Fonctions de rappel du client.

Sur les connexions de génération 2025

ctx.session.create_message(...) et ctx.session.list_roots() existent toujours pour le code qui pilote la session directement. Elles ne fonctionnent que là où un canal de retour existe (connexions de génération 2025 qui ne sont pas sans état), et les appeler déclenche un avertissement d’obsolescence. Les marqueurs de résolveur ci-dessus sont la forme prise en charge : ils choisissent le mode d’acheminement d’après la version négociée et n’émettent pas d’avertissement.

Récapitulatif

  • Renvoyez Sample(...) ou ListRoots() depuis un résolveur ; l’outil reçoit le CreateMessageResult ou le ListRootsResult comme n’importe quelle autre dépendance.
  • Le client doit déclarer la capacité correspondante, sinon l’appel échoue avec -32021 au lieu qu’une requête soit envoyée.
  • Les deux fonctionnalités sont obsolètes en version 2026-07-28 : pleinement fonctionnelles pour l’instant, inadaptées aux nouvelles conceptions. Préférez les API des fournisseurs à l’échantillonnage et les paramètres explicites aux racines.

Indiquer l’avancement d’un outil lent : Progression.