Annulation
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 client peut abandonner un appel : l’utilisateur a appuyé sur stop, ou un délai d’attente a expiré.
Dans ce cas, le SDK annule votre gestionnaire (handler). Le await sur lequel il est en attente lève une exception, la fonction est dépilée, et rien de ce qu’elle renvoie n’est envoyé. La plupart des gestionnaires n’ont rien à faire de particulier.
Deux catégories font exception : un gestionnaire qui a quelque chose à nettoyer, et un gestionnaire écrit comme un simple def.
Nettoyer dans un outil async def
Placez le nettoyage dans un finally :
import anyio
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
holds: set[str] = set()
async def take_payment(title: str) -> None:
await anyio.sleep(30) # the customer is typing a card number
async def release_hold(title: str) -> None:
await anyio.sleep(0.1) # a round trip to the stock system
holds.discard(title)
@mcp.tool()
async def order_book(title: str) -> str:
"""Hold a copy of a book while the customer pays for it."""
holds.add(title)
try:
await take_payment(title)
return f"Ordered {title!r}."
finally:
with anyio.move_on_after(5, shield=True):
await release_hold(title)
- Le
finallys’exécute quelle que soit la façon dont l’outil se termine : il a renvoyé une valeur, il a levé une exception ou il a été annulé. - Un nettoyage qui doit faire un
awaita besoin deshield=True. Dans un gestionnaire annulé, chaqueawaitsuivant lève lui aussi une exception : sans cette protection,release_holds’arrêterait dès sa première ligne. - Rien ne peut annuler un bloc protégé, alors donnez-lui une limite de temps. Ici, elle est de
5secondes.
Tip
Utilisez finally, pas except. L’annulation doit continuer à remonter une fois votre nettoyage
terminé, et un finally la laisse passer.
S’arrêter tôt dans un outil def simple
Un outil def simple s’exécute dans un thread, et rien ne peut interrompre un thread de l’extérieur. C’est à l’outil de poser la question :
import time
import anyio.from_thread
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
offline: set[str] = set()
def index_book(title: str) -> None:
time.sleep(1) # slow work with nothing to await
@mcp.tool()
def rebuild_index(titles: list[str]) -> str:
"""Take search offline and rebuild its index, one book at a time."""
offline.add("search")
try:
for title in titles:
anyio.from_thread.check_cancelled()
index_book(title)
return f"Indexed {len(titles)} books."
finally:
offline.discard("search")
anyio.from_thread.check_cancelled()ne fait rien tant que l’appel est en cours, et lève une exception une fois qu’il a été annulé. Appelez-la entre deux unités de travail.- Ici aussi, le nettoyage va dans un
finally. Dans un thread, il n’y a aucune attente asynchrone, donc aucune protection n’est nécessaire. - Un outil
defqui ne pose jamais la question s’exécute jusqu’au bout, et son résultat est jeté.
Où cela s’applique
Les fonctions de prompt et de ressource sont annulées exactement comme les outils.
Cela fonctionne de la même façon avec stdio et Streamable HTTP. Avec la classe Client de ce SDK, abandonner revient à annuler la tâche qui attend call_tool, ou à laisser son délai read_timeout_seconds expirer.
Warning
Deux options de Streamable HTTP empêchent la nouvelle d’atteindre votre gestionnaire : json_response=True sur une
connexion 2026-07-28, et stateless_http=True sur une connexion historique. Dans ces cas, le gestionnaire s’exécute
jusqu’au bout, quoi qu’ait fait le client.
Récapitulatif
- Quand le client abandonne un appel, le SDK annule le gestionnaire : outil, prompt ou ressource.
async def: nettoyez dans unfinally, et placez tout nettoyage qui comporte une attente asynchrone dansanyio.move_on_after(seconds, shield=True).defsimple : appelezanyio.from_thread.check_cancelled()entre deux unités de travail, sinon l’outil s’exécute jusqu’au bout. Un simplefinallyse charge du nettoyage.json_response=True(connexions modernes) etstateless_http=True(connexions historiques) désactivent l’annulation.
La progression et l’annulation se jouent entre un outil en cours d’exécution et son appelant. Les lignes qu’il journalise pour vous, la personne qui exploite le serveur, passent par un autre canal : Journalisation.