Skip to content

Commit 4291be2

Browse files
authored
Merge pull request jlowin#56 from jurasofish/decorator_typing
Decorator typing
2 parents 1d97563 + a81688a commit 4291be2

File tree

2 files changed

+15
-8
lines changed

2 files changed

+15
-8
lines changed

src/fastmcp/py.typed

Whitespace-only changes.

src/fastmcp/server.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import json
77
import re
88
from itertools import chain
9-
from typing import Any, Callable, Dict, Literal, Sequence
9+
from typing import Any, Callable, Dict, Literal, Sequence, TypeVar, ParamSpec
1010

1111
import pydantic_core
1212
from pydantic import Field
@@ -40,13 +40,18 @@
4040

4141
from fastmcp.exceptions import ResourceError
4242
from fastmcp.prompts import Prompt, PromptManager
43+
from fastmcp.prompts.base import PromptResult
4344
from fastmcp.resources import FunctionResource, Resource, ResourceManager
4445
from fastmcp.tools import ToolManager
4546
from fastmcp.utilities.logging import configure_logging, get_logger
4647
from fastmcp.utilities.types import Image
4748

4849
logger = get_logger(__name__)
4950

51+
P = ParamSpec("P")
52+
R = TypeVar("R")
53+
R_PromptResult = TypeVar("R_PromptResult", bound=PromptResult)
54+
5055

5156
class 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

Comments
 (0)