PrefectHQ/fastmcp · error · TypeError

Cannot specify both a name as first argument and as keyword

Error message

Cannot specify both a name as first argument and as keyword argument.

What it means

The shared @app.tool() / @app.ui() decorator accepts a tool name either as the first positional argument or as the `name` keyword — but not both. If a string is passed positionally and a `name=` keyword is also supplied, this TypeError is raised because the intent is ambiguous.

Source

Thrown at fastmcp_slim/fastmcp/apps/app.py:126

        raise ValueError(f"Cannot resolve tool reference: {fn!r}")

    return _resolve_tool_ref


def _dispatch_decorator(
    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


# ---------------------------------------------------------------------------

View on GitHub (pinned to 1f02114297)

Solutions

  1. Keep only one of the two: remove the keyword `name=` if a positional string is given.
  2. If both names were intentional (e.g. UI name differs), pass the intended one and register the other explicitly.
  3. Use @app.tool() with no args and rely on the function __name__ when no custom name is needed.

Example fix

// before
@app.tool("upload_file", name="upload")
def upload_file(...): ...
// after
@app.tool("upload")
def upload_file(...): ...
Defensive patterns

Strategy: validation

Validate before calling

def check_tool_decorator_args(name_or_fn, name):
    if isinstance(name_or_fn, str) and name is not None:
        raise TypeError("Provide the tool name either positionally or as name=, not both")

Type guard

def has_conflicting_names(name_or_fn, name) -> bool:
    return isinstance(name_or_fn, str) and name is not None

Try / catch

try:
    deco = app.tool("upload", name="upload2")
except TypeError as e:
    if "both a name" in str(e):
        deco = app.tool("upload")

Prevention

When it happens

Trigger: Calling `@app.tool("my_name", name="other_name")` or the equivalent with app.ui — a positional string name plus a keyword name.

Common situations: Refactoring from keyword style to positional (or vice versa) and leaving both in place; merging code changes that each added a name; copy-pasting decorator examples.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/e909c486560e6d0c. Report an issue: GitHub.