Fehler behandeln
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 Tool kann auf zwei Arten scheitern, und das SDK behandelt sie sehr unterschiedlich.
Löse eine gewöhnliche Exception aus, und das Modell sieht sie. Löse MCPError aus, und das Protokoll sieht sie.
Auf dieser Seite geht es um die Wahl zwischen beiden.
Ein Fehler, den das Modell beheben kann
Nimm ein Tool, das etwas nachschlägt, und lass das Nachschlagen ins Leere laufen:
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.tool()
def get_author(title: str) -> str:
"""Look up the author of a book in the catalog."""
if title not in CATALOG:
raise ValueError(f"No book titled {title!r} in the catalog.")
return CATALOG[title]
An diesen zwei Zeilen ist nichts MCP-Spezifisches. get_author löst einen schlichten ValueError aus, so wie es jede Python-Funktion täte.
Ruf es mit einem Titel auf, der nicht im Katalog steht, und sieh dir das Ergebnis an:
result.is_error # True
result.content # [TextContent(text="Error executing tool get_author: No book titled 'Nothing' in the catalog.")]
result.structured_content # None
- Der Request war erfolgreich. Es gibt ein Ergebnis; beim Aufrufer wurde nichts ausgelöst.
is_erroristTrue, und die Meldung deiner Exception (mit dem Tool-Namen als Präfix) steht incontent– genau dort, wo das Modell liest.structured_contentistNone. Ein fehlgeschlagener Aufruf hat keinen Rückgabewert, den man strukturieren könnte.
Das ist ein Tool-Fehler, und er ist der Standard für jede Exception, die dein Tool auslöst. Fast immer ist es auch genau das, was du willst.
Das Modell ist es, das dein Tool aufruft. Es hat die Argumente gewählt. Ein Tool-Fehler ist also ein Zug im Gespräch: Das Modell liest „No book titled 'Nothing' in the catalog.“, merkt, dass es den Titel falsch geraten hat, und ruft erneut mit einem besseren auf. Du hast ein einziges raise geschrieben und einen sich selbst korrigierenden Agenten bekommen.
Tip
Gib aus einem Tool nie eine Fehlermeldung per return zurück. Ein zurückgegebener String hat is_error=False;
für das Modell (und für jede Client-UI) sieht es also aus, als hätte das Tool funktioniert und dieser String
wäre die Antwort. raise. Das Flag ist das Signal.
Ein Fehler, den das Modell nicht beheben kann
Tausche jetzt ValueError gegen MCPError.
from mcp import MCPError
from mcp.server import MCPServer
from mcp.types import INVALID_PARAMS
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.tool()
def get_author(title: str) -> str:
"""Look up the author of a book in the catalog."""
if title not in CATALOG:
raise MCPError(code=INVALID_PARAMS, message=f"No book titled {title!r} in the catalog.")
return CATALOG[title]
MCPError ist der Protokollfehler des SDK. Es ist die eine Exception, die der Tool-Wrapper nicht abfängt: Sie wird weitergereicht, und der ganze tools/call-Request schlägt mit einem JSON-RPC-Fehler fehl statt mit einem Ergebnis zu enden.
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog."
}
- Es gibt kein Ergebnis. Kein
content, keinis_error: nichts, was das Modell lesen könnte. - Stattdessen bekommt die Host-Anwendung den Fehler – genauso, als gäbe es das Tool gar nicht.
code,messageunddatakommen unverändert an.INVALID_PARAMSist-32602;mcp.typesexportiert ihn und die anderen JSON-RPC-Fehlercodes (INVALID_REQUEST,INTERNAL_ERROR, ...) als Konstanten, sodass du nie eine magische Zahl tippen musst.
Check
Dasselbe Nachschlagen, derselbe Fehlschlag, aber jetzt löst der Aufruf auf der Client-Seite eine Exception aus, statt zurückzukehren:
mcp.shared.exceptions.MCPError: No book titled 'Nothing' in the catalog.
Die erste Version gab dem Modell einen Satz, auf den es reagieren konnte. Diese hier gibt ihm nichts.
Für get_author ist das eindeutig schlechter – und genau darum geht es im nächsten Abschnitt.
Welche der beiden auslösen
Die beiden Wege beantworten zwei verschiedene Fragen.
- Löse irgendeine Exception aus bei einem Fehlschlag der Ausführung: Das, was dein Tool versucht hat, hat nicht geklappt. Das Modell hat den Aufruf gewählt, also sollte das Modell die Folge sehen und die Chance bekommen, sich zu fangen. Ein falsch geschriebener Titel, eine vorgelagerte API mit Timeout, eine Zeile, die es nicht gibt: alles Tool-Fehler.
- Löse
MCPErroraus, wenn der Request selbst abgelehnt werden soll: Dem Client fehlt eine Capability, auf die dein Tool angewiesen ist, der Server ist nicht in einem Zustand, irgendwen zu bedienen, der Aufrufer hat einen erforderlichen Schritt übersprungen. Kein erneuter Versuch des Modells behebt irgendetwas davon, also bringt es nichts, ihm die Meldung zu geben.
Eine Frage entscheidet: Hätte ein klügeres Modell das vermeiden können? Ja -> gewöhnliche Exception. Nein -> MCPError.
Nach diesem Test hat die zweite Version von get_author die falsche Wahl getroffen: Ein besserer Titel behebt das Problem, also hätte das Modell die Meldung sehen sollen. Sie soll dir den Mechanismus zeigen, nicht ihn empfehlen.
Info
MCPError findest du unter from mcp import MCPError; sie nimmt code, message und eine optionale
data-Payload entgegen. Was immer du hineinlegst, bekommt der Client: Das SDK leitet eine ausgelöste
MCPError wortwörtlich weiter, statt sie zu bereinigen.
Eine Ressource, die es nicht gibt
Ressourcen ziehen dieselbe Grenze und bringen für den häufigen Fall eine benannte Exception mit.
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ResourceNotFoundError
mcp = MCPServer("Bookshop")
CATALOG = {"Dune": "Frank Herbert", "Neuromancer": "William Gibson"}
@mcp.resource("books://{title}")
def book(title: str) -> str:
"""The catalog entry for one book."""
if title not in CATALOG:
raise ResourceNotFoundError(f"No book titled {title!r} in the catalog.")
return f"{title} by {CATALOG[title]}"
books://{title} ist ein Template. Es passt auf jeden Titel, also sind „der URI ist wohlgeformt“ und „das Buch existiert“ zwei verschiedene Fragen, und nur deine Funktion kann die zweite beantworten.
Wenn sie das nicht kann, löse ResourceNotFoundError aus. Das SDK macht daraus den Protokollfehler, den die Spezifikation einer fehlenden Ressource zuordnet: -32602 mit dem angeforderten URI in data, damit der Client weiß, welcher Lesevorgang fehlgeschlagen ist.
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog.",
"data": {"uri": "books://Nothing"}
}
Beachte, dass es hier kein halbes Ergebnis mit is_error=True gibt. Das Lesen einer Ressource liefert entweder Inhalte oder schlägt fehl: Ressourcen haben nur den Protokollweg. Templates und alles Weitere zu Ressourcen stehen in Ressourcen.
Fehler, die du nie auslöst
Ein ungültiges Argument erreicht deine Funktion nie.
Schick get_author einen title, der kein String ist, und das SDK weist ihn anhand des Eingabeschemas ab, bevor es dich aufruft – als dieselbe Art Tool-Fehler mit is_error=True, den das Modell lesen und korrigieren kann. Tools zeigt dieselbe Ablehnung mit einer Field(le=50)-Einschränkung.
Das bedeutet eine ganze Klasse von raise-Anweisungen, die du nicht schreibst: Validiere deine eigenen Type Hints nicht noch einmal.
Info
Alles auf dieser Seite ist das, was ein Client sieht, und der In-Memory-Client, mit dem du
Tests schreibst, sieht exakt dasselbe. Selbst raise_exceptions=True macht aus einem Tool-Fehler
keinen Traceback mehr: Bis dieses Flag greifen könnte, ist deine Exception längst das
Ergebnis mit is_error=True. Prüfe das Ergebnis mit Assertions. Testen beschreibt das Muster.
Zusammenfassung
- Löse irgendeine Exception in einem Tool aus -> der Aufruf gibt
is_error=Truemit deiner Meldung incontentzurück. Das Modell liest sie und kann es erneut versuchen. Das ist der Standard. - Löse
MCPErroraus -> der Aufruf selbst schlägt mit einem JSON-RPC-Fehler fehl. Das Modell sieht nichts; der Host kümmert sich darum.code,messageunddatakommen unverändert durch. - Die entscheidende Frage: Hätte ein klügeres Modell das vermeiden können? Ja -> Exception. Nein ->
MCPError. ResourceNotFoundErroraus einem Ressourcen-Handler -> das-32602des Protokolls, mit dem URI indata.- Ungültige Argumente werden anhand des Schemas abgewiesen, bevor deine Funktion läuft; dafür schreibst du kein
raise. from mcp import MCPError; die Fehlercode-Konstanten kommen ausmcp.types.
Fehler behandelt. Das ist alles, was ein Server nach außen anbietet. Was jeder Handler lesen und während der Ausführung zurück an den Client tun kann, ist der nächste Abschnitt: Im Handler.
Den genauen Wortlaut der SDK-Fehler, denen du am ehesten begegnest, was jeder bedeutet und wie du ihn jeweils mit einem Handgriff behebst, findest du unter Fehlerbehebung.