Topic 6: Long-running work
9 min read·22 Sept 2026
Progress and cooperative cancellation
Module 5 covered the handler side: await ctx.report_progress(done, total, message) streams notifications/progress while the tool runs, and it is a no-op when the caller did not ask. Cancellation in Python async code is cooperative: the runtime can only stop your handler at an await, so a long loop needs regular await points, and state must be consistent at each one. run_import checkpoints before its sleep, so a cancellation there loses nothing.
The client that every Part B to E demo uses shows the client half: answering the confirmation and printing progress.
python
"""Call import_notes through the balancer: a confirmation round, then the import with progress."""
from __future__ import annotations
import json
import sys
from pathlib import Path
import anyio
from mcp import Client
from mcp.client import ClientRequestContext
from mcp.shared.exceptions import MCPError
from mcp.types import ElicitRequestParams, ElicitResult
BATCH = Path(__file__).parent / "data" / "m09_import_batch.json"
async def confirm(context: ClientRequestContext, params: ElicitRequestParams) -> ElicitResult:
print(f" question from server: {params.message!r} -> answering yes")
return ElicitResult(action="accept", content={"ok": True})
async def show_progress(progress: float, total: float | None, message: str | None) -> None:
print(f" progress {progress:.0f}/{total:.0f}: {message}")
async def main(url: str, key: str, count: int, mode: str = "auto") -> None:
items = json.loads(BATCH.read_text(encoding="utf-8"))[:count]
async with Client(url, elicitation_callback=confirm, mode=mode) as client:
try:
result = await client.call_tool(
"import_notes", {"items": items, "idempotency_key": key}, progress_callback=show_progress
)
except MCPError as exc:
print(f" MCPError {exc.code}: {exc.message}")
return
print(" result:", json.dumps(result.structured_content))
if __name__ == "__main__":
anyio.run(main, sys.argv[1], sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 2)