2626
2727import asyncio
2828import random
29- from typing import Any , Literal
29+ from typing import Annotated , Any , Literal
3030
3131from mcp .server .fastmcp import Context , FastMCP
3232from mcp .server .session import ServerSession
3333from mcp .types import Icon , ToolAnnotations
34+ from pydantic import Field
3435
3536from .icons import (
3637 ABACUS_ICON ,
@@ -67,12 +68,10 @@ def register_tools(mcp: FastMCP) -> None:
6768 ),
6869 ],
6970 )
70- def hello (name : str ) -> str :
71- """A friendly greeting tool that says hello to someone.
72-
73- Args:
74- name: The name to greet
75- """
71+ def hello (
72+ name : Annotated [str , Field (title = "Name" , description = "Name of the person to greet" )],
73+ ) -> str :
74+ """Say hello to a person"""
7675 return f"Hello, { name } ! Welcome to MCP."
7776
7877 @mcp .tool (
@@ -91,15 +90,13 @@ def hello(name: str) -> str:
9190 ),
9291 ],
9392 )
94- def get_weather (location : str ) -> dict [str , Any ]:
95- """Get current weather for a location (simulated).
96-
97- Args:
98- location: City name or coordinates
99- """
93+ def get_weather (
94+ city : Annotated [str , Field (title = "City" , description = "City name to get weather for" )],
95+ ) -> dict [str , Any ]:
96+ """Get the current weather for a city"""
10097 conditions = ["sunny" , "cloudy" , "rainy" , "windy" ]
10198 return {
102- "location" : location ,
99+ "location" : city ,
103100 "temperature" : round (15 + random .random () * 20 ),
104101 "unit" : "celsius" ,
105102 "conditions" : random .choice (conditions ),
@@ -123,16 +120,15 @@ def get_weather(location: str) -> dict[str, Any]:
123120 ],
124121 )
125122 async def ask_llm (
126- prompt : str ,
123+ prompt : Annotated [
124+ str , Field (title = "Prompt" , description = "The question or prompt to send to the LLM" )
125+ ],
127126 ctx : Context [ServerSession , None ],
128- max_tokens : int = 100 ,
127+ maxTokens : Annotated [
128+ int , Field (title = "Max Tokens" , description = "Maximum tokens in response" )
129+ ] = 100 ,
129130 ) -> str :
130- """Ask the connected LLM a question using sampling.
131-
132- Args:
133- prompt: The question or prompt for the LLM
134- max_tokens: Maximum tokens in response
135- """
131+ """Ask the connected LLM a question using sampling"""
136132 try :
137133 result = await ctx .session .create_message (
138134 messages = [
@@ -141,7 +137,7 @@ async def ask_llm(
141137 "content" : {"type" : "text" , "text" : prompt },
142138 }
143139 ],
144- max_tokens = max_tokens ,
140+ max_tokens = maxTokens ,
145141 )
146142 if result .content .type == "text" :
147143 return f"LLM Response: { result .content .text } "
@@ -166,17 +162,12 @@ async def ask_llm(
166162 ],
167163 )
168164 async def long_task (
169- task_name : str ,
165+ taskName : Annotated [ str , Field ( title = "Task Name" , description = "Name for this task" )] ,
170166 ctx : Context [ServerSession , None ],
167+ steps : Annotated [int , Field (title = "Steps" , description = "Number of steps to simulate" )] = 5 ,
171168 ) -> str :
172- """A task that takes 5 seconds and reports progress along the way.
173-
174- Args:
175- task_name: Name for this task
176- """
177- steps = 5
178-
179- await ctx .info (f"Starting task: { task_name } " )
169+ """Simulate a long-running task with progress updates"""
170+ await ctx .info (f"Starting task: { taskName } " )
180171
181172 for i in range (steps ):
182173 await ctx .report_progress (
@@ -188,7 +179,7 @@ async def long_task(
188179
189180 await ctx .report_progress (progress = 1.0 , total = 1.0 , message = "Complete!" )
190181
191- return f'Task "{ task_name } " completed successfully after { steps } steps!'
182+ return f'Task "{ taskName } " completed successfully after { steps } steps!'
192183
193184 @mcp .tool (
194185 annotations = ToolAnnotations (
@@ -207,7 +198,7 @@ async def long_task(
207198 ],
208199 )
209200 async def load_bonus_tool (ctx : Context [ServerSession , None ]) -> str :
210- """Dynamically loads a bonus tool that wasn't available at startup. """
201+ """Dynamically register a new bonus tool"""
211202 global _bonus_tool_loaded
212203
213204 if _bonus_tool_loaded :
@@ -284,14 +275,16 @@ def bonus_calculator(a: float, b: float, operation: Operation) -> str:
284275 ),
285276 )
286277 async def confirm_action (
287- action : str ,
278+ action : Annotated [
279+ str , Field (title = "Action" , description = "Description of the action to confirm" )
280+ ],
288281 ctx : Context [ServerSession , None ],
282+ destructive : Annotated [
283+ bool ,
284+ Field (title = "Destructive" , description = "Whether the action is destructive" ),
285+ ] = False ,
289286 ) -> str :
290- """Demonstrates elicitation - requests user confirmation before proceeding.
291-
292- Args:
293- action: The action to confirm with the user
294- """
287+ """Request user confirmation before proceeding"""
295288 try :
296289 # Form elicitation: Display a structured form with typed fields
297290 # The client renders this as a dialog/form based on the JSON schema
@@ -338,17 +331,15 @@ async def confirm_action(
338331 ),
339332 )
340333 async def get_feedback (
334+ question : Annotated [
335+ str , Field (title = "Question" , description = "The question to ask the user" )
336+ ],
341337 ctx : Context [ServerSession , None ],
342- topic : str = "" ,
343338 ) -> str :
344- """Demonstrates URL elicitation - opens a feedback form in the browser.
345-
346- Args:
347- topic: Optional topic for the feedback
348- """
339+ """Request feedback from the user"""
349340 feedback_url = "https://github.com/SamMorrowDrums/mcp-starters/issues/new?template=workshop-feedback.yml"
350- if topic :
351- feedback_url += f"&title={ topic } "
341+ if question :
342+ feedback_url += f"&title={ question } "
352343
353344 try :
354345 # URL elicitation: Open a web page in the user's browser
0 commit comments