22
33from collections .abc import AsyncIterator
44from contextlib import asynccontextmanager
5- from dataclasses import dataclass
65from typing import Any
76
87import anyio
98import click
109import mcp .types as types
1110import uvicorn
12- from anyio . abc import TaskGroup
11+ from mcp . server . experimental . task_context import ServerTaskContext
1312from mcp .server .lowlevel import Server
1413from mcp .server .streamable_http_manager import StreamableHTTPSessionManager
15- from mcp .shared .experimental .tasks .helpers import task_execution
16- from mcp .shared .experimental .tasks .in_memory_task_store import InMemoryTaskStore
1714from starlette .applications import Starlette
1815from starlette .routing import Mount
1916
17+ server = Server ("simple-task-server" )
2018
21- @dataclass
22- class AppContext :
23- task_group : TaskGroup
24- store : InMemoryTaskStore
25-
26-
27- @asynccontextmanager
28- async def lifespan (server : Server [AppContext , Any ]) -> AsyncIterator [AppContext ]:
29- store = InMemoryTaskStore ()
30- async with anyio .create_task_group () as tg :
31- yield AppContext (task_group = tg , store = store )
32- store .cleanup ()
33-
34-
35- server : Server [AppContext , Any ] = Server ("simple-task-server" , lifespan = lifespan )
19+ # One-line setup: auto-registers get_task, get_task_result, list_tasks, cancel_task
20+ server .experimental .enable_tasks ()
3621
3722
3823@server .list_tools ()
@@ -50,61 +35,21 @@ async def list_tools() -> list[types.Tool]:
5035@server .call_tool ()
5136async def handle_call_tool (name : str , arguments : dict [str , Any ]) -> list [types .TextContent ] | types .CreateTaskResult :
5237 ctx = server .request_context
53- app = ctx .lifespan_context
54-
55- # Validate task mode - raises McpError(-32601) if client didn't use task augmentation
5638 ctx .experimental .validate_task_mode (types .TASK_REQUIRED )
5739
58- # Create the task
59- metadata = ctx .experimental .task_metadata
60- assert metadata is not None
61- task = await app .store .create_task (metadata )
62-
63- # Spawn background work
64- async def do_work () -> None :
65- async with task_execution (task .taskId , app .store ) as task_ctx :
66- await task_ctx .update_status ("Starting work..." )
67- await anyio .sleep (1 )
68-
69- await task_ctx .update_status ("Processing step 1..." )
70- await anyio .sleep (1 )
71-
72- await task_ctx .update_status ("Processing step 2..." )
73- await anyio .sleep (1 )
74-
75- await task_ctx .complete (
76- types .CallToolResult (content = [types .TextContent (type = "text" , text = "Task completed!" )])
77- )
78-
79- app .task_group .start_soon (do_work )
80- return types .CreateTaskResult (task = task )
81-
82-
83- @server .experimental .get_task ()
84- async def handle_get_task (request : types .GetTaskRequest ) -> types .GetTaskResult :
85- app = server .request_context .lifespan_context
86- task = await app .store .get_task (request .params .taskId )
87- if task is None :
88- raise ValueError (f"Task { request .params .taskId } not found" )
89- return types .GetTaskResult (
90- taskId = task .taskId ,
91- status = task .status ,
92- statusMessage = task .statusMessage ,
93- createdAt = task .createdAt ,
94- lastUpdatedAt = task .lastUpdatedAt ,
95- ttl = task .ttl ,
96- pollInterval = task .pollInterval ,
97- )
40+ async def work (task : ServerTaskContext ) -> types .CallToolResult :
41+ await task .update_status ("Starting work..." )
42+ await anyio .sleep (1 )
43+
44+ await task .update_status ("Processing step 1..." )
45+ await anyio .sleep (1 )
46+
47+ await task .update_status ("Processing step 2..." )
48+ await anyio .sleep (1 )
9849
50+ return types .CallToolResult (content = [types .TextContent (type = "text" , text = "Task completed!" )])
9951
100- @server .experimental .get_task_result ()
101- async def handle_get_task_result (request : types .GetTaskPayloadRequest ) -> types .GetTaskPayloadResult :
102- app = server .request_context .lifespan_context
103- result = await app .store .get_result (request .params .taskId )
104- if result is None :
105- raise ValueError (f"Result for task { request .params .taskId } not found" )
106- assert isinstance (result , types .CallToolResult )
107- return types .GetTaskPayloadResult (** result .model_dump ())
52+ return await ctx .experimental .run_task (work )
10853
10954
11055@click .command ()
0 commit comments