Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/fastmcp/prompts/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ def from_function(
if isinstance(fn, staticmethod):
fn = fn.__func__

# Validate that task=True requires async functions (after unwrapping)
if task and not inspect.iscoroutinefunction(fn):
raise ValueError(
f"Prompt '{func_name}' uses a sync function but has task=True. "
"Background tasks require async functions. Set task=False to disable."
)

# Wrap fn to handle dependency resolution internally
wrapped_fn = without_injected_parameters(fn)
type_adapter = get_cached_typeadapter(wrapped_fn)
Expand Down
13 changes: 13 additions & 0 deletions src/fastmcp/resources/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,19 @@ def from_function(
if isinstance(uri, str):
uri = AnyUrl(uri)

# Validate that task=True requires async functions
# Handle callable classes and staticmethods before checking
fn_to_check = fn
if not inspect.isroutine(fn) and callable(fn):
fn_to_check = fn.__call__
if isinstance(fn_to_check, staticmethod):
fn_to_check = fn_to_check.__func__
if task and not inspect.iscoroutinefunction(fn_to_check):
raise ValueError(
f"Resource '{name or get_fn_name(fn)}' uses a sync function but has task=True. "
"Background tasks require async functions. Set task=False to disable."
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Wrap fn to handle dependency resolution internally
wrapped_fn = without_injected_parameters(fn)

Expand Down
7 changes: 7 additions & 0 deletions src/fastmcp/resources/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,13 @@ def from_function(
if isinstance(fn, staticmethod):
fn = fn.__func__

# Validate that task=True requires async functions (after unwrapping)
if task and not inspect.iscoroutinefunction(fn):
raise ValueError(
f"Resource template '{func_name}' uses a sync function but has task=True. "
"Background tasks require async functions. Set task=False to disable."
)

wrapper_fn = without_injected_parameters(fn)
type_adapter = get_cached_typeadapter(wrapper_fn)
parameters = type_adapter.json_schema()
Expand Down
31 changes: 0 additions & 31 deletions src/fastmcp/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from __future__ import annotations

import asyncio
import inspect
import re
import secrets
Expand Down Expand Up @@ -1787,16 +1786,6 @@ def my_tool(x: int) -> str:
task if task is not None else self._support_tasks_by_default
)

# Disable task support for sync functions (Docket requires async)
if supports_task and not asyncio.iscoroutinefunction(fn):
if task is True:
# User explicitly requested task=True for sync function
logger.warning(
f"Tool '{tool_name or fn.__name__}' has task=True but is synchronous. "
"Background task support requires async functions. Disabling task support."
)
supports_task = False

# Register the tool immediately and return the tool object
# Note: Deprecation warning for exclude_args is handled in Tool.from_function
tool = Tool.from_function(
Expand Down Expand Up @@ -1988,16 +1977,6 @@ def decorator(fn: AnyFunction) -> Resource | ResourceTemplate:
task if task is not None else self._support_tasks_by_default
)

# Disable task support for sync functions (Docket requires async)
if supports_task and not asyncio.iscoroutinefunction(fn):
if task is True:
# User explicitly requested task=True for sync function
logger.warning(
f"Resource '{uri}' has task=True but is synchronous. "
"Background task support requires async functions. Disabling task support."
)
supports_task = False

# Check if this should be a template
has_uri_params = "{" in uri and "}" in uri
# Use wrapper to check for user-facing parameters
Expand Down Expand Up @@ -2207,16 +2186,6 @@ def another_prompt(data: str) -> list[Message]:
task if task is not None else self._support_tasks_by_default
)

# Disable task support for sync functions (Docket requires async)
if supports_task and not asyncio.iscoroutinefunction(fn):
if task is True:
# User explicitly requested task=True for sync function
logger.warning(
f"Prompt '{prompt_name or fn.__name__}' has task=True but is synchronous. "
"Background task support requires async functions. Disabling task support."
)
supports_task = False

# Register the prompt immediately
prompt = Prompt.from_function(
fn=fn,
Expand Down
14 changes: 14 additions & 0 deletions src/fastmcp/tools/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,20 @@ def from_function(
stacklevel=2,
)

# Validate that task=True requires async functions
# Handle callable classes and staticmethods before checking
fn_to_check = fn
if not inspect.isroutine(fn) and callable(fn):
fn_to_check = fn.__call__
if isinstance(fn_to_check, staticmethod):
fn_to_check = fn_to_check.__func__
if task and not inspect.iscoroutinefunction(fn_to_check):
fn_name = name or getattr(fn, "__name__", repr(fn))
raise ValueError(
f"Tool '{fn_name}' uses a sync function but has task=True. "
"Background tasks require async functions. Set task=False to disable."
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

parsed_fn = ParsedFunction.from_function(fn, exclude_args=exclude_args)

if name is None and parsed_fn.name == "<lambda>":
Expand Down
Loading
Loading