Zum Inhalt

Sampling und Roots

Maschinelle Übersetzung

Diese Seite wurde automatisch aus der englischen Dokumentation übersetzt, und die englische Seite ist die maßgebliche Fassung. Wenn sich etwas falsch liest, erklärt Übersetzungen, wie du es melden kannst.

Ein Handler kann den verbundenen Client um zwei weitere Dinge bitten: eine Completion vom eigenen Modell des Clients (Sampling) und die Arbeitsverzeichnisse des Clients (Roots, freigegebene Arbeitsverzeichnisse).

Beides funktioniert weiterhin, auf jeder Protokollversion, die das SDK spricht. Lies aber die Warnung, bevor du dein Design darauf aufbaust:

Veraltet seit der Spezifikation 2026-07-28

Sampling und Roots gelten seit 2026-07-28 als veraltet (SEP-2577). Sie bleiben voll funktionsfähig und stehen noch mindestens zwölf Monate in der Spezifikation, bevor sie entfernt werden dürfen, aber neue Implementierungen sollten nicht mehr darauf aufbauen. Die empfohlenen Migrationen: Binde statt Sampling direkt die API deines LLM-Anbieters an, und übergib Verzeichnisse statt über Roots per Tool-Parameter, Ressourcen-URI oder Serverkonfiguration. Die SDK-weite Liste steht in Veraltete Features.

Sampling: das Modell des Clients ausleihen

Ein Resolver gibt Sample(...) zurück, und das Tool erhält die Completion – über denselben Abhängigkeitsmechanismus, der in Abhängigkeiten Elicit ausführt:

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=...) spiegelt die Parameter von sampling/createMessage wider. Der injizierte Wert ist das CreateMessageResult des Clients; übergibst du tools oder tool_choice, wird daraus stattdessen ein CreateMessageResultWithTools.
  • Der Client muss die Capability sampling deklariert haben (sampling.tools, wenn du tools oder tool_choice übergibst). Hat er das nicht, schlägt der Aufruf mit einem Protokollfehler -32021 fehl, statt einen Request zu senden, den der Client nicht verarbeiten kann. Eine Session aus der Zeit vor 2026 ohne Rückkanal (back-channel) schlägt mit ihrem üblichen No-Back-Channel-Fehler fehl, weil es nichts gibt, worüber gesendet werden könnte.
  • Bei 2026-07-28 wird der Request innerhalb des Multi-Roundtrip-Ablaufs zugestellt (Multi-Roundtrip-Requests (multi-round-trip requests)); bei 2025-11-25 ist er ein eigenständiger Request an den Client. Der Code ist in beiden Fällen derselbe, beachte aber die Multi-Roundtrip-Regel: Der Request muss in jeder Wiederholungsrunde identisch aussehen. Baue ihn deshalb nur aus den Argumenten des Tools und anderen stabilen Daten.
  • Lass include_context unangetastet: Andere Werte als "none" sind selbst veraltet (SEP-2596) und brauchen eine Capability, die fast kein Client deklariert.

Roots: Wohin damit?

Roots sind die Verzeichnisse, auf denen der Server laut Client arbeiten darf. Sie sind ein informativer Hinweis, kein Mechanismus zur Zugriffskontrolle. Ein Resolver gibt ListRoots() zurück:

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)
  • Das injizierte ListRootsResult enthält eine Liste von Root-Objekten: jeweils einen file://-URI und einen optionalen Anzeigenamen.
  • Die Hürde ist dieselbe wie beim Sampling: Ohne deklarierte Capability roots schlägt der Aufruf mit -32021 fehl, statt den Request zu senden.

Auf der anderen Seite der Leitung beantwortet der Client beide Requests mit den Callbacks, die er ohnehin schon hat: sampling_callback und list_roots_callback, beschrieben in Client-Callbacks.

Auf Verbindungen der 2025er-Generation

ctx.session.create_message(...) und ctx.session.list_roots() gibt es weiterhin für Code, der die Session direkt ansteuert. Sie funktionieren nur dort, wo ein Rückkanal existiert (nicht zustandslose Verbindungen der 2025er-Generation), und ihr Aufruf löst eine Deprecation-Warnung aus. Die Resolver-Marker oben sind die unterstützte Form: Sie wählen die Zustellung anhand der ausgehandelten Version und warnen nicht.

Zusammenfassung

  • Gib Sample(...) oder ListRoots() aus einem Resolver zurück; das Tool erhält das CreateMessageResult oder ListRootsResult wie jede andere Abhängigkeit.
  • Der Client muss die passende Capability deklarieren, sonst schlägt der Aufruf mit -32021 fehl, statt dass ein Request gesendet wird.
  • Beide Features sind bei 2026-07-28 veraltet: vorerst voll funktionsfähig, aber falsch für neue Designs. Bevorzuge Anbieter-APIs gegenüber Sampling und explizite Parameter gegenüber Roots.

Wie ein langsames Tool seinen Fortschritt meldet: Fortschritt.