Skip to content

Commit e68f821

Browse files
committed
Refactor example servers to have simpler tool dispatch
Extract tool-specific logic into separate handler functions, keeping the call_tool decorator handler simple - it just dispatches based on tool name and returns an error for unknown tools.
1 parent e8c7c8a commit e68f821

File tree

2 files changed

+70
-49
lines changed
  • examples/servers
    • simple-task-interactive/mcp_simple_task_interactive
    • simple-task/mcp_simple_task

2 files changed

+70
-49
lines changed

examples/servers/simple-task-interactive/mcp_simple_task_interactive/server.py

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -46,69 +46,78 @@ async def list_tools() -> list[types.Tool]:
4646
]
4747

4848

49-
@server.call_tool()
50-
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> types.CallToolResult | types.CreateTaskResult:
49+
async def handle_confirm_delete(arguments: dict[str, Any]) -> types.CreateTaskResult:
50+
"""Handle the confirm_delete tool - demonstrates elicitation."""
5151
ctx = server.request_context
52-
53-
# Validate task mode - this tool requires task augmentation
5452
ctx.experimental.validate_task_mode(types.TASK_REQUIRED)
5553

56-
if name == "confirm_delete":
57-
filename = arguments.get("filename", "unknown.txt")
58-
print(f"\n[Server] confirm_delete called for '{filename}'")
54+
filename = arguments.get("filename", "unknown.txt")
55+
print(f"\n[Server] confirm_delete called for '{filename}'")
5956

60-
async def do_confirm(task: ServerTaskContext) -> types.CallToolResult:
61-
print(f"[Server] Task {task.task_id} starting elicitation...")
57+
async def work(task: ServerTaskContext) -> types.CallToolResult:
58+
print(f"[Server] Task {task.task_id} starting elicitation...")
6259

63-
result = await task.elicit(
64-
message=f"Are you sure you want to delete '{filename}'?",
65-
requestedSchema={
66-
"type": "object",
67-
"properties": {"confirm": {"type": "boolean"}},
68-
"required": ["confirm"],
69-
},
70-
)
60+
result = await task.elicit(
61+
message=f"Are you sure you want to delete '{filename}'?",
62+
requestedSchema={
63+
"type": "object",
64+
"properties": {"confirm": {"type": "boolean"}},
65+
"required": ["confirm"],
66+
},
67+
)
7168

72-
print(f"[Server] Received elicitation response: action={result.action}, content={result.content}")
69+
print(f"[Server] Received elicitation response: action={result.action}, content={result.content}")
7370

74-
if result.action == "accept" and result.content:
75-
confirmed = result.content.get("confirm", False)
76-
text = f"Deleted '{filename}'" if confirmed else "Deletion cancelled"
77-
else:
78-
text = "Deletion cancelled"
71+
if result.action == "accept" and result.content:
72+
confirmed = result.content.get("confirm", False)
73+
text = f"Deleted '{filename}'" if confirmed else "Deletion cancelled"
74+
else:
75+
text = "Deletion cancelled"
7976

80-
print(f"[Server] Completing task with result: {text}")
81-
return types.CallToolResult(content=[types.TextContent(type="text", text=text)])
77+
print(f"[Server] Completing task with result: {text}")
78+
return types.CallToolResult(content=[types.TextContent(type="text", text=text)])
8279

83-
# run_task creates the task, spawns work, returns CreateTaskResult immediately
84-
return await ctx.experimental.run_task(do_confirm)
80+
return await ctx.experimental.run_task(work)
8581

86-
elif name == "write_haiku":
87-
topic = arguments.get("topic", "nature")
88-
print(f"\n[Server] write_haiku called for topic '{topic}'")
8982

90-
async def do_haiku(task: ServerTaskContext) -> types.CallToolResult:
91-
print(f"[Server] Task {task.task_id} starting sampling...")
83+
async def handle_write_haiku(arguments: dict[str, Any]) -> types.CreateTaskResult:
84+
"""Handle the write_haiku tool - demonstrates sampling."""
85+
ctx = server.request_context
86+
ctx.experimental.validate_task_mode(types.TASK_REQUIRED)
87+
88+
topic = arguments.get("topic", "nature")
89+
print(f"\n[Server] write_haiku called for topic '{topic}'")
90+
91+
async def work(task: ServerTaskContext) -> types.CallToolResult:
92+
print(f"[Server] Task {task.task_id} starting sampling...")
93+
94+
result = await task.create_message(
95+
messages=[
96+
types.SamplingMessage(
97+
role="user",
98+
content=types.TextContent(type="text", text=f"Write a haiku about {topic}"),
99+
)
100+
],
101+
max_tokens=50,
102+
)
92103

93-
result = await task.create_message(
94-
messages=[
95-
types.SamplingMessage(
96-
role="user",
97-
content=types.TextContent(type="text", text=f"Write a haiku about {topic}"),
98-
)
99-
],
100-
max_tokens=50,
101-
)
104+
haiku = "No response"
105+
if isinstance(result.content, types.TextContent):
106+
haiku = result.content.text
102107

103-
haiku = "No response"
104-
if isinstance(result.content, types.TextContent):
105-
haiku = result.content.text
108+
print(f"[Server] Received sampling response: {haiku[:50]}...")
109+
return types.CallToolResult(content=[types.TextContent(type="text", text=f"Haiku:\n{haiku}")])
106110

107-
print(f"[Server] Received sampling response: {haiku[:50]}...")
108-
return types.CallToolResult(content=[types.TextContent(type="text", text=f"Haiku:\n{haiku}")])
111+
return await ctx.experimental.run_task(work)
109112

110-
return await ctx.experimental.run_task(do_haiku)
111113

114+
@server.call_tool()
115+
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> types.CallToolResult | types.CreateTaskResult:
116+
"""Dispatch tool calls to their handlers."""
117+
if name == "confirm_delete":
118+
return await handle_confirm_delete(arguments)
119+
elif name == "write_haiku":
120+
return await handle_write_haiku(arguments)
112121
else:
113122
return types.CallToolResult(
114123
content=[types.TextContent(type="text", text=f"Unknown tool: {name}")],

examples/servers/simple-task/mcp_simple_task/server.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ async def list_tools() -> list[types.Tool]:
3232
]
3333

3434

35-
@server.call_tool()
36-
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> list[types.TextContent] | types.CreateTaskResult:
35+
async def handle_long_running_task(arguments: dict[str, Any]) -> types.CreateTaskResult:
36+
"""Handle the long_running_task tool - demonstrates status updates."""
3737
ctx = server.request_context
3838
ctx.experimental.validate_task_mode(types.TASK_REQUIRED)
3939

@@ -52,6 +52,18 @@ async def work(task: ServerTaskContext) -> types.CallToolResult:
5252
return await ctx.experimental.run_task(work)
5353

5454

55+
@server.call_tool()
56+
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> types.CallToolResult | types.CreateTaskResult:
57+
"""Dispatch tool calls to their handlers."""
58+
if name == "long_running_task":
59+
return await handle_long_running_task(arguments)
60+
else:
61+
return types.CallToolResult(
62+
content=[types.TextContent(type="text", text=f"Unknown tool: {name}")],
63+
isError=True,
64+
)
65+
66+
5567
@click.command()
5668
@click.option("--port", default=8000, help="Port to listen on")
5769
def main(port: int) -> int:

0 commit comments

Comments
 (0)