PrefectHQ/fastmcp · error
Functions with **kwargs are not supported as tools
Error message
Functions with **kwargs are not supported as tools
What it means
Tools reject `**kwargs` because every tool argument must have a named schema entry in the input schema; VAR_KEYWORD parameters have no fixed names and can't be validated or documented, so `FunctionTool.from_function` raises ValueError when it encounters one.
Source
Thrown at fastmcp_slim/fastmcp/tools/function_parsing.py:270
validate: bool = True,
wrap_non_object_output_schema: bool = True,
) -> ParsedFunction:
if validate:
sig = inspect.signature(fn)
# Reject signatures that cannot be represented by MCP's
# object-shaped tool arguments.
for param in sig.parameters.values():
if param.kind == inspect.Parameter.POSITIONAL_ONLY:
raise ValueError(
"Functions with positional-only parameters are not "
"supported as tools because MCP passes tool arguments by "
"name. Replace them with standard parameters that can be "
"passed as keywords."
)
if param.kind == inspect.Parameter.VAR_POSITIONAL:
raise ValueError("Functions with *args are not supported as tools")
if param.kind == inspect.Parameter.VAR_KEYWORD:
raise ValueError(
"Functions with **kwargs are not supported as tools"
)
# collect name and description before we potentially modify the function
fn_name = getattr(fn, "__name__", None) or fn.__class__.__name__
outer_docstring = parse_docstring(fn)
# if the fn is a callable class, we need to get the __call__ method from here out
if not inspect.isroutine(fn) and not isinstance(fn, functools.partial):
fn = fn.__call__
# if the fn is a staticmethod, we need to work with the underlying function
if isinstance(fn, staticmethod):
fn = fn.__func__
# For callable classes, parameter descriptions must come from
# __call__'s docstring — where the exposed parameters are actually
# declared. The class docstring's Args section, if any, typically
# describes __init__, so falling back to it would risk injectingView on GitHub (pinned to 1f02114297)
Solutions
- Replace `**kwargs` with explicit named, typed parameters.
- If arbitrary options are genuinely needed, accept a single `options: dict[str, Any]` parameter and validate its keys manually.
- Split the generic function into concrete tool functions with fixed signatures.
Example fix
# before def configure(host, **options): ... # after def configure(host, timeout: int = 30, retries: int = 3): ...
Defensive patterns
Strategy: validation
Validate before calling
import inspect
def tool_ready(fn) -> bool:
return not any(p.kind == inspect.Parameter.VAR_KEYWORD
for p in inspect.signature(fn).parameters.values()) Try / catch
try:
tool = FunctionTool.from_function(fn)
except ValueError as e:
if "**kwargs" in str(e):
raise TypeError(f"{fn.__name__} must not use **kwargs; declare explicit options")
raise Prevention
- Avoid **kwargs in tool-facing functions; use explicit options or an options dict param.
- Inspect signatures before registering in dynamic tool-loading code.
- Keep decorators from silently adding kwargs to tool functions.
When it happens
Trigger: `FunctionTool.from_function(fn)` or `@mcp.tool` on functions like `def configure(host, **options)`; generic forwarding wrappers; decorator-generated functions that capture kwargs.
Common situations: Exposing config-style or pass-through wrapper functions; maintaining one generic function for many tools instead of explicit per-tool signatures; migrating an old JSON-RPC handler that accepted arbitrary payloads.
Related errors
- Functions with positional-only parameters are not supported
- Functions with *args are not supported as tools
- Cannot resolve tool reference: {fn!r}
- Cannot determine tool name for {fn!r}
- File {name!r} not found. Available: {available}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/5647a63e2e109b86.
Report an issue: GitHub.