Cancelación
Traducción automática
Esta página se tradujo automáticamente a partir de la documentación en inglés, y la página en inglés es la versión de referencia. Si algo no se lee bien, Traducciones explica cómo avisarnos.
Un cliente puede abandonar una llamada: el usuario pulsó detener o se agotó un timeout.
Cuando lo hace, el SDK cancela tu handler. El await en el que está esperando lanza una excepción, la ejecución sale de la función y no se envía nada de lo que devuelva. La mayoría de los handlers no necesita hacer nada al respecto.
Dos tipos sí: un handler que tiene algo que limpiar y un handler que es un def simple.
Limpiar en una herramienta async def
Pon la limpieza en 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)
- El
finallyse ejecuta termine como termine la herramienta: devolvió un valor, lanzó una excepción o se canceló. - La limpieza que tiene que hacer
awaitnecesitashield=True. En un handler cancelado, cadaawaitposterior también lanza una excepción, así que sin el escudorelease_holdse detendría en su primera línea. - Nada puede cancelar un bloque protegido con escudo, así que ponle un límite de tiempo. Aquí son
5segundos.
Tip
Usa finally, no except. La cancelación tiene que seguir propagándose hacia arriba una vez
terminada la limpieza, y un finally se lo permite.
Detenerse antes en una herramienta def simple
Una herramienta def simple se ejecuta en un hilo, y nada puede interrumpir un hilo desde fuera. La herramienta tiene que preguntar:
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()no hace nada mientras la llamada sigue activa, y lanza una excepción una vez que se ha cancelado. Llámala entre unidades de trabajo.- Aquí la limpieza también va en un
finally. En un hilo no hay esperas asíncronas, así que no necesita escudo. - Una herramienta
defque nunca pregunta se ejecuta hasta el final, y su resultado se descarta.
Dónde se aplica
Las funciones de prompts y de recursos se cancelan exactamente igual que las herramientas.
Funciona igual sobre stdio y Streamable HTTP. Con el Client de este SDK, abandonar significa cancelar la tarea que espera call_tool o dejar que se agote su read_timeout_seconds.
Warning
Dos opciones de Streamable HTTP impiden que la noticia llegue a tu handler: json_response=True
en una conexión 2026-07-28 y stateless_http=True en una heredada. Ahí el handler se ejecuta
hasta el final, haya hecho lo que haya hecho el cliente.
Resumen
- Cuando el cliente abandona una llamada, el SDK cancela el handler: herramienta, prompt o recurso.
async def: limpia en unfinallyy pon la limpieza con esperas asíncronas dentro deanyio.move_on_after(seconds, shield=True).defsimple: llama aanyio.from_thread.check_cancelled()entre unidades de trabajo, o la herramienta se ejecuta hasta el final. Unfinallysimple hace la limpieza.json_response=True(conexiones modernas) ystateless_http=True(las heredadas) desactivan la cancelación.
El progreso y la cancelación son cosa de una herramienta en ejecución y de quien la llama. Las líneas que registra para ti, la persona que opera el servidor, son un canal distinto: Registro de logs.