"""Progress, in-flight logging, and cancellation from a single long-running tool.""" import anyio import mcp_types as types from mcp.server.mcpserver import Context, MCPServer from stories._hosting import run_server_from_args def build_server() -> MCPServer: mcp = MCPServer("streaming-example") @mcp.tool() async def countdown(steps: int, ctx: Context) -> dict[str, int]: """Emit one progress + one log notification per step; observes cancellation.""" try: for i in range(1, steps + 1): await ctx.report_progress(float(i), float(steps), f"step {i}/{steps}") # No non-deprecated logging helper on Context yet, so send the raw # notification. `related_request_id` keeps it on this request's response # stream (matters over streamable HTTP). await ctx.request_context.session.send_notification( types.LoggingMessageNotification( params=types.LoggingMessageNotificationParams( level="info", logger="countdown", data=f"step {i}/{steps}" ) ), related_request_id=ctx.request_context.request_id, ) except anyio.get_cancelled_exc_class(): # The client abandoned the call. Release resources here, then re-raise so # the dispatcher unwinds the request — never swallow cancellation. raise return {"completed": steps, "total": steps} return mcp if __name__ == "__main__": run_server_from_args(build_server)