PrefectHQ/fastmcp · error · TypeError
First argument to @{decorator_name} must be a function, stri
Error message
First argument to @{decorator_name} must be a function, string, or None, got {type(name_or_fn)} What it means
@app.tool() and @app.ui() use _dispatch_decorator to accept a function, a name string, or nothing. Any other first argument (e.g. a bool, dict, class, or an accidentally invoked decorator) cannot be interpreted and raises this TypeError.
Source
Thrown at fastmcp_slim/fastmcp/apps/app.py:133
name_or_fn: str | AnyFunction | None,
name: str | None,
register: Callable[[Any, str | None], Any],
decorator_name: str,
) -> Any:
"""Shared dispatch logic for @app.tool() and @app.ui() calling patterns."""
if inspect.isroutine(name_or_fn):
return register(name_or_fn, name)
if isinstance(name_or_fn, str):
if name is not None:
raise TypeError(
"Cannot specify both a name as first argument and as keyword argument."
)
tool_name: str | None = name_or_fn
elif name_or_fn is None:
tool_name = name
else:
raise TypeError(
f"First argument to @{decorator_name} must be a function, string, or None, "
f"got {type(name_or_fn)}"
)
def decorator(fn: F) -> F:
return register(fn, tool_name)
return decorator
# ---------------------------------------------------------------------------
# FastMCPApp
# ---------------------------------------------------------------------------
class FastMCPApp(Provider):
"""A Provider that represents an MCP application.
View on GitHub (pinned to 1f02114297)
Solutions
- Pass only a function, a string tool name, or nothing: `@app.tool`, `@app.tool("name")`, or `@app.tool()`.
- Ensure the decorated target is a plain function/routine (use staticmethod-free, plain defs).
- Check for stray positional arguments (e.g. moving kwargs like description into the correct supported signature).
Example fix
// before
@app.tool({"name": "upload"})
def upload(...): ...
// after
@app.tool("upload")
def upload(...): ... Defensive patterns
Strategy: type-guard
Validate before calling
def is_valid_decorator_arg(x) -> bool:
import inspect
return x is None or isinstance(x, str) or inspect.isroutine(x) Type guard
import inspect
def is_decorator_target(x) -> bool:
return x is None or isinstance(x, str) or inspect.isroutine(x) Try / catch
try:
app.tool(target)
except TypeError as e:
if "First argument to @" in str(e):
raise ValueError(f"Unsupported decorator target: {target!r}") from e Prevention
- Always decorate plain functions with @app.tool / @app.ui
- Use parentheses form @app.tool("name") or @app.tool() only
- Pass config options as keywords, never positionally
- Check that prior decorator stages return functions, not other objects
When it happens
Trigger: Calling `@app.tool(some_object)` where some_object is not a callable routine, string, or None — commonly `@app.tool` misuse like `@app.tool(description="...")`-style kwargs not supported, or `app.tool(True)`, or forgetting parentheses vs adding wrong ones (`@app.tool()` vs `@app.tool`).
Common situations: Passing configuration options positionally instead of by keyword; decorating a class or non-function object; double-application where the first application returned something other than a function.
Related errors
- Cannot specify both a name as first argument and as keyword
- A completion handler returned a str; return a list of string
- Got unexpected keyword argument(s): {', '.join(sorted(unknow
- Version must be a string, int, or float, got bool: {v!r}
- Version must be a string, int, or float, got {type(v).__name
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/a0571965ec847936.
Report an issue: GitHub.