Pular para conteúdo

Cancelamento

Tradução automática

Esta página foi traduzida automaticamente a partir da documentação em inglês, e a página em inglês é a versão de referência. Se algo parecer errado, Traduções explica como avisar.

Um cliente pode desistir de uma chamada: o usuário apertou o botão de parar, ou um timeout se esgotou.

Quando isso acontece, o SDK cancela o seu handler. O await em que ele está esperando lança uma exceção, a função é desempilhada, e nada do que ela retornar é enviado. A maioria dos handlers não precisa fazer nada a respeito.

Dois tipos precisam: um handler com algo para limpar, e um handler que é um def comum.

Faça a limpeza em uma ferramenta async def

Coloque a limpeza em um finally:

server.py
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)
  • O finally executa não importa como a ferramenta termine: ela retornou, lançou uma exceção ou foi cancelada.
  • Uma limpeza que precisa fazer await exige shield=True. Em um handler cancelado, todo await seguinte também lança uma exceção, então sem a proteção release_hold pararia na primeira linha.
  • Nada consegue cancelar um bloco protegido, então dê a ele um limite de tempo. Aqui são 5 segundos.

Tip

Use finally, não except. O cancelamento precisa continuar subindo depois que a sua limpeza termina, e um finally permite isso.

Pare antes do fim em uma ferramenta def comum

Uma ferramenta def comum roda em uma thread, e nada consegue interromper uma thread de fora. A ferramenta precisa perguntar:

server.py
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() não faz nada enquanto a chamada está ativa, e lança uma exceção assim que ela é cancelada. Chame essa função entre unidades de trabalho.
  • Aqui a limpeza também vai em um finally. Nada em uma thread faz await, então a limpeza não precisa de proteção.
  • Uma ferramenta def que nunca pergunta roda até o fim, e o resultado dela é descartado.

Onde se aplica

As funções de prompt e de recurso são canceladas exatamente como as ferramentas.

Funciona do mesmo jeito sobre stdio e Streamable HTTP. Com o Client deste SDK, desistir significa cancelar a tarefa que aguarda call_tool, ou deixar o read_timeout_seconds da chamada se esgotar.

Warning

Duas opções do Streamable HTTP impedem que a notícia chegue ao seu handler: json_response=True em uma conexão 2026-07-28, e stateless_http=True em uma conexão legada. Nesses casos, o handler roda até o fim, não importa o que o cliente tenha feito.

Resumo

  • Quando o cliente desiste de uma chamada, o SDK cancela o handler: ferramenta, prompt ou recurso.
  • async def: faça a limpeza em um finally, e coloque a limpeza que faz await dentro de anyio.move_on_after(seconds, shield=True).
  • def comum: chame anyio.from_thread.check_cancelled() entre unidades de trabalho, ou a ferramenta roda até o fim. Um finally simples faz a limpeza.
  • json_response=True (conexões modernas) e stateless_http=True (conexões legadas) desligam o cancelamento.

Progresso e cancelamento ficam entre uma ferramenta em execução e quem a chamou. As linhas que ela registra em log para você, a pessoa que opera o servidor, são um canal diferente: Logging.