mjorkestra commited on
Commit
e9d217d
·
1 Parent(s): 4edf1c9

Improve decorator typing

Browse files
Files changed (1) hide show
  1. src/fastmcp/server.py +15 -8
src/fastmcp/server.py CHANGED
@@ -6,7 +6,7 @@ import inspect
6
  import json
7
  import re
8
  from itertools import chain
9
- from typing import Any, Callable, Dict, Literal, Sequence
10
 
11
  import pydantic_core
12
  from pydantic import Field
@@ -40,6 +40,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
40
 
41
  from fastmcp.exceptions import ResourceError
42
  from fastmcp.prompts import Prompt, PromptManager
 
43
  from fastmcp.resources import FunctionResource, Resource, ResourceManager
44
  from fastmcp.tools import ToolManager
45
  from fastmcp.utilities.logging import configure_logging, get_logger
@@ -47,6 +48,10 @@ from fastmcp.utilities.types import Image
47
 
48
  logger = get_logger(__name__)
49
 
 
 
 
 
50
 
51
  class Settings(BaseSettings):
52
  """FastMCP server settings.
@@ -222,7 +227,9 @@ class FastMCP:
222
  """
223
  self._tool_manager.add_tool(fn, name=name, description=description)
224
 
225
- def tool(self, name: str | None = None, description: str | None = None) -> Callable:
 
 
226
  """Decorator to register a tool.
227
 
228
  Tools can optionally request a Context object by adding a parameter with the Context type annotation.
@@ -254,7 +261,7 @@ class FastMCP:
254
  "Did you forget to call it? Use @tool() instead of @tool"
255
  )
256
 
257
- def decorator(fn: Callable) -> Callable:
258
  self.add_tool(fn, name=name, description=description)
259
  return fn
260
 
@@ -275,7 +282,7 @@ class FastMCP:
275
  name: str | None = None,
276
  description: str | None = None,
277
  mime_type: str | None = None,
278
- ) -> Callable:
279
  """Decorator to register a function as a resource.
280
 
281
  The function will be called when the resource is read to generate its content.
@@ -309,9 +316,9 @@ class FastMCP:
309
  "Did you forget to call it? Use @resource('uri') instead of @resource"
310
  )
311
 
312
- def decorator(fn: Callable) -> Callable:
313
  @functools.wraps(fn)
314
- def wrapper(*args: Any, **kwargs: Any) -> Any:
315
  return fn(*args, **kwargs)
316
 
317
  # Check if this should be a template
@@ -361,7 +368,7 @@ class FastMCP:
361
 
362
  def prompt(
363
  self, name: str | None = None, description: str | None = None
364
- ) -> Callable:
365
  """Decorator to register a prompt.
366
 
367
  Args:
@@ -402,7 +409,7 @@ class FastMCP:
402
  "Did you forget to call it? Use @prompt() instead of @prompt"
403
  )
404
 
405
- def decorator(func: Callable) -> Callable:
406
  prompt = Prompt.from_function(func, name=name, description=description)
407
  self.add_prompt(prompt)
408
  return func
 
6
  import json
7
  import re
8
  from itertools import chain
9
+ from typing import Any, Callable, Dict, Literal, Sequence, TypeVar, ParamSpec
10
 
11
  import pydantic_core
12
  from pydantic import Field
 
40
 
41
  from fastmcp.exceptions import ResourceError
42
  from fastmcp.prompts import Prompt, PromptManager
43
+ from fastmcp.prompts.base import PromptResult
44
  from fastmcp.resources import FunctionResource, Resource, ResourceManager
45
  from fastmcp.tools import ToolManager
46
  from fastmcp.utilities.logging import configure_logging, get_logger
 
48
 
49
  logger = get_logger(__name__)
50
 
51
+ P = ParamSpec("P")
52
+ R = TypeVar("R")
53
+ R_PromptResult = TypeVar("R_PromptResult", bound=PromptResult)
54
+
55
 
56
  class Settings(BaseSettings):
57
  """FastMCP server settings.
 
227
  """
228
  self._tool_manager.add_tool(fn, name=name, description=description)
229
 
230
+ def tool(
231
+ self, name: str | None = None, description: str | None = None
232
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
233
  """Decorator to register a tool.
234
 
235
  Tools can optionally request a Context object by adding a parameter with the Context type annotation.
 
261
  "Did you forget to call it? Use @tool() instead of @tool"
262
  )
263
 
264
+ def decorator(fn: Callable[P, R]) -> Callable[P, R]:
265
  self.add_tool(fn, name=name, description=description)
266
  return fn
267
 
 
282
  name: str | None = None,
283
  description: str | None = None,
284
  mime_type: str | None = None,
285
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
286
  """Decorator to register a function as a resource.
287
 
288
  The function will be called when the resource is read to generate its content.
 
316
  "Did you forget to call it? Use @resource('uri') instead of @resource"
317
  )
318
 
319
+ def decorator(fn: Callable[P, R]) -> Callable[P, R]:
320
  @functools.wraps(fn)
321
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
322
  return fn(*args, **kwargs)
323
 
324
  # Check if this should be a template
 
368
 
369
  def prompt(
370
  self, name: str | None = None, description: str | None = None
371
+ ) -> Callable[[Callable[P, R_PromptResult]], Callable[P, R_PromptResult]]:
372
  """Decorator to register a prompt.
373
 
374
  Args:
 
409
  "Did you forget to call it? Use @prompt() instead of @prompt"
410
  )
411
 
412
+ def decorator(func: Callable[P, R_PromptResult]) -> Callable[P, R_PromptResult]:
413
  prompt = Prompt.from_function(func, name=name, description=description)
414
  self.add_prompt(prompt)
415
  return func