PrefectHQ/fastmcp · error · ValueError

Cannot determine tool name for {fn!r}

Error message

Cannot determine tool name for {fn!r}

What it means

When registering a tool, its name is taken from the explicit tool_name argument or the callable's __name__. If both are missing (the object has no __name__), _register cannot produce a unique tool name and raises this ValueError.

Source

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

            @app.tool
            def save(name: str): ...

            @app.tool()
            def save(name: str): ...

            @app.tool("custom_name")
            def save(name: str): ...
        """
        visibility: list[Literal["app", "model"]] = (
            ["app", "model"] if model else ["app"]
        )

        def _register(fn: F, tool_name: str | None) -> F:
            from fastmcp.tools.base import Tool

            resolved_name = tool_name or getattr(fn, "__name__", None)
            if resolved_name is None:
                raise ValueError(f"Cannot determine tool name for {fn!r}")

            from fastmcp.apps.config import AppConfig, app_config_to_meta_dict
            from fastmcp.server.providers.addressing import (
                TOOL_HASH_META_KEY,
                hash_tool,
            )

            app_config = AppConfig(visibility=visibility)
            meta: dict[str, Any] = {
                "ui": app_config_to_meta_dict(app_config),
                "fastmcp": {
                    "app": self.name,
                    TOOL_HASH_META_KEY: hash_tool(self.name, resolved_name),
                },
            }

            tool_obj = Tool.from_function(
                fn,

View on GitHub (pinned to 1f02114297)

Solutions

  1. Pass an explicit tool name: `@app.tool("my_tool_name")`.
  2. Restore __name__ on the callable (use functools.wraps in your decorator, or set `fn.__name__ = ...`).
  3. Register the underlying plain function instead of the wrapper object.

Example fix

// before
@app.tool()
def wrapper(): return partial_fn()
// after
@app.tool("my_tool")
def my_tool(): ...
# or: functools.wraps(original) inside the custom decorator
Defensive patterns

Strategy: validation

Validate before calling

def ensure_nameable(fn, tool_name=None) -> None:
    if tool_name is None and getattr(fn, "__name__", None) is None:
        raise ValueError(f"Pass an explicit tool name for {fn!r}")

Type guard

def has_derivable_name(fn) -> bool:
    return getattr(fn, "__name__", None) is not None

Try / catch

try:
    app.tool()(fn)
except ValueError as e:
    if "Cannot determine tool name" in str(e):
        app.tool("explicit_name")(fn)

Prevention

When it happens

Trigger: Registering a callable lacking __name__ (e.g. functools.partial, a callable class instance, a custom decorator that did not use functools.wraps) without passing an explicit name.

Common situations: Decorating callables wrapped by third-party libraries; registering method-like objects; minified/proxied functions in tests (Mocks).

Related errors


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