66import json
77import re
88from itertools import chain
9- from typing import Any , Callable , Dict , Literal , Sequence
9+ from typing import Any , Callable , Dict , Literal , Sequence , TypeVar , ParamSpec
1010
1111import pydantic_core
1212from pydantic import Field
4040
4141from fastmcp .exceptions import ResourceError
4242from fastmcp .prompts import Prompt , PromptManager
43+ from fastmcp .prompts .base import PromptResult
4344from fastmcp .resources import FunctionResource , Resource , ResourceManager
4445from fastmcp .tools import ToolManager
4546from fastmcp .utilities .logging import configure_logging , get_logger
4647from fastmcp .utilities .types import Image
4748
4849logger = get_logger (__name__ )
4950
51+ P = ParamSpec ("P" )
52+ R = TypeVar ("R" )
53+ R_PromptResult = TypeVar ("R_PromptResult" , bound = PromptResult )
54+
5055
5156class Settings (BaseSettings ):
5257 """FastMCP server settings.
@@ -222,7 +227,9 @@ def add_tool(
222227 """
223228 self ._tool_manager .add_tool (fn , name = name , description = description )
224229
225- def tool (self , name : str | None = None , description : str | None = None ) -> Callable :
230+ def tool (
231+ self , name : str | None = None , description : str | None = None
232+ ) -> Callable [[Callable [P , R ]], Callable [P , R ]]:
226233 """Decorator to register a tool.
227234
228235 Tools can optionally request a Context object by adding a parameter with the Context type annotation.
@@ -254,7 +261,7 @@ async def async_tool(x: int, context: Context) -> str:
254261 "Did you forget to call it? Use @tool() instead of @tool"
255262 )
256263
257- def decorator (fn : Callable ) -> Callable :
264+ def decorator (fn : Callable [ P , R ] ) -> Callable [ P , R ] :
258265 self .add_tool (fn , name = name , description = description )
259266 return fn
260267
@@ -275,7 +282,7 @@ def resource(
275282 name : str | None = None ,
276283 description : str | None = None ,
277284 mime_type : str | None = None ,
278- ) -> Callable :
285+ ) -> Callable [[ Callable [ P , R ]], Callable [ P , R ]] :
279286 """Decorator to register a function as a resource.
280287
281288 The function will be called when the resource is read to generate its content.
@@ -309,9 +316,9 @@ def get_weather(city: str) -> str:
309316 "Did you forget to call it? Use @resource('uri') instead of @resource"
310317 )
311318
312- def decorator (fn : Callable ) -> Callable :
319+ def decorator (fn : Callable [ P , R ] ) -> Callable [ P , R ] :
313320 @functools .wraps (fn )
314- def wrapper (* args : Any , ** kwargs : Any ) -> Any :
321+ def wrapper (* args : P . args , ** kwargs : P . kwargs ) -> R :
315322 return fn (* args , ** kwargs )
316323
317324 # Check if this should be a template
@@ -361,7 +368,7 @@ def add_prompt(self, prompt: Prompt) -> None:
361368
362369 def prompt (
363370 self , name : str | None = None , description : str | None = None
364- ) -> Callable :
371+ ) -> Callable [[ Callable [ P , R_PromptResult ]], Callable [ P , R_PromptResult ]] :
365372 """Decorator to register a prompt.
366373
367374 Args:
@@ -402,7 +409,7 @@ async def analyze_file(path: str) -> list[Message]:
402409 "Did you forget to call it? Use @prompt() instead of @prompt"
403410 )
404411
405- def decorator (func : Callable ) -> Callable :
412+ def decorator (func : Callable [ P , R_PromptResult ] ) -> Callable [ P , R_PromptResult ] :
406413 prompt = Prompt .from_function (func , name = name , description = description )
407414 self .add_prompt (prompt )
408415 return func
0 commit comments