跳轉至

取消

機器翻譯

本頁是從英文說明文件自動翻譯而來,以英文頁面為準。如果哪裡讀起來不對勁,翻譯有說明如何回報。

用戶端可以放棄一次呼叫:使用者按了停止,或是逾時時間到了。

這時 SDK 會取消你的處理函式。它正在等待的那個 await 會引發例外,函式逐層退出,它回傳的任何東西都不會送出。大多數處理函式不需要為此做任何事。

有兩種需要:有東西要清理的處理函式,以及用普通 def 寫的處理函式。

在 async def 工具中清理

把清理工作放進 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)
  • 不論工具怎麼結束,finally 都會執行:正常回傳、引發例外,或是被取消。
  • 需要 await 的清理工作要加上 shield=True。在已取消的處理函式裡,之後的每個 await 也都會引發例外,所以少了這層保護,release_hold 會在第一行就停住。
  • 受保護的區塊無法被任何東西取消,所以要給它一個時間限制。這裡是 5 秒。

Tip

用 finally,不要用 except。清理完成後,取消必須繼續往上傳遞,而 finally 會放行。

在普通 def 工具中提早停止

普通的 def 工具在執行緒中執行,而執行緒無法從外部中斷。工具必須自己詢問:

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() 在呼叫仍有效時什麼都不做,一旦呼叫被取消就會引發例外。在每個工作單元之間呼叫它。
  • 這裡的清理工作同樣放在 finally 裡。執行緒裡不會有任何 await,所以不需要保護。
  • 從不詢問的 def 工具會一路執行到結束,結果則被丟棄。

適用範圍

提示詞和資源函式被取消的方式和工具完全相同。

在 stdio 和 Streamable HTTP 上的運作方式相同。使用這個 SDK 的 Client 時,放棄指的是取消正在等待 call_tool 的任務,或是讓它的 read_timeout_seconds 時間用完。

Warning

有兩個 Streamable HTTP 選項會讓處理函式無從得知取消:2026-07-28 連線上的 json_response=True,以及舊版連線上的 stateless_http=True。這時不論用戶端做了什麼,處理函式都會執行到結束。

重點回顧

  • 用戶端放棄呼叫時,SDK 會取消處理函式:工具、提示詞或資源都一樣。
  • async def:在 finally 裡清理,需要 await 的清理工作放進 anyio.move_on_after(seconds, shield=True)。
  • 普通 def:在工作單元之間呼叫 anyio.from_thread.check_cancelled(),否則工具會執行到結束。清理用一般的 finally 即可。
  • json_response=True(新版連線)和 stateless_http=True(舊版連線)會關閉取消功能。

進度與取消是執行中的工具和它的呼叫端之間的事。它為你這個伺服器操作者寫下的記錄,走的是另一個管道:記錄。