PrefectHQ/fastmcp · error
Functions with positional-only parameters are not supported
Error message
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.
What it means
MCP tool arguments arrive as a JSON object keyed by parameter name, so every tool parameter must be callable by keyword. Parameters declared positional-only (`def f(x, /)`) can't be supplied that way, so `FunctionTool.from_function` rejects such signatures with ValueError before the tool is registered.
Source
Thrown at fastmcp_slim/fastmcp/tools/function_parsing.py:261
description: str | None
input_schema: dict[str, Any]
output_schema: dict[str, Any] | None
return_type: Any = None
@classmethod
def from_function(
cls,
fn: Callable[..., Any],
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):View on GitHub (pinned to 1f02114297)
Solutions
- Rewrite the function to drop the `/` marker so parameters accept keyword arguments.
- Wrap the function in a keyword-friendly adapter and register the adapter as the tool.
- Use functools.partial or a lambda with keyword params to re-expose the function if you can't modify it.
- If it's a third-party function, submit/patch upstream to accept keywords, or shim locally.
Example fix
# before def query(sql, /, limit=10): ... tool = FunctionTool.from_function(query) # after def query(sql, limit=10): ... tool = FunctionTool.from_function(query)
Defensive patterns
Strategy: validation
Validate before calling
import inspect
def tool_ready(fn) -> bool:
return not any(p.kind == inspect.Parameter.POSITIONAL_ONLY
for p in inspect.signature(fn).parameters.values()) Try / catch
try:
tool = FunctionTool.from_function(fn)
except ValueError as e:
if "positional-only" in str(e):
tool = FunctionTool.from_function(lambda *a, **kw: fn(*a), name=fn.__name__)
else:
raise Prevention
- Avoid `/` in functions intended as tools.
- Validate signatures at import time with an inspection helper.
- Write adapters for third-party functions with positional-only params.
When it happens
Trigger: `FunctionTool.from_function(fn)` (or decorating with `@mcp.tool`) on a function with `/`-delimited positional-only params, e.g. `def query(sql, /, limit=10)`; frequently seen on bound methods or wrappers from libraries using positional-only markers (common in stdlib-style code since Python 3.8+).
Common situations: Exposing a third-party function that uses positional-only syntax; copy-pasting C-extension-like signatures; writing `def f(a, /)` intentionally for perf/API stability in your own code and then registering it as a tool.
Related errors
- Functions with *args are not supported as tools
- Functions with **kwargs 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/8bd01767faf3d83e.
Report an issue: GitHub.