PrefectHQ/fastmcp · error · ValueError

Cannot resolve tool reference: {fn!r}

Error message

Cannot resolve tool reference: {fn!r}

What it means

_resolve_tool_ref converts a tool reference (name string, callable, or Tool object) into a ResolvedTool. When the passed object is neither a string, a Tool, nor a callable carrying a __name__ attribute, resolution fails and this ValueError is raised. It protects the @app.tool registry from unresolvable references.

Source

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

        fmeta: Any = None
        try:
            from fastmcp.decorators import get_fastmcp_meta

            fmeta = get_fastmcp_meta(fn)
        except Exception:
            pass

        if fmeta is not None:
            name: str | None = getattr(fmeta, "name", None)
            if name is not None:
                return ResolvedTool(name=_prefix(name))

        fn_name = getattr(fn, "__name__", None)
        if fn_name is not None:
            return ResolvedTool(name=_prefix(fn_name))

        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."

View on GitHub (pinned to 1f02114297)

Solutions

  1. Pass the tool's name as a plain string instead of the object.
  2. Ensure the callable has a __name__ attribute (apply functools.wraps in your decorator, or use the raw function).
  3. Pass the actual Tool instance rather than a wrapper around it.

Example fix

// before
ref = functools.partial(my_tool, flag=True)
app.tool_ref(ref)
// after
ref = functools.partial(my_tool, flag=True)
ref.__name__ = my_tool.__name__  # or pass "my_tool" as a string
app.tool_ref(ref)
Defensive patterns

Strategy: type-guard

Validate before calling

def is_resolvable_tool_ref(fn) -> bool:
    if isinstance(fn, str):
        return True
    return callable(fn) and getattr(fn, "__name__", None) is not None

Type guard

def is_tool_ref(obj) -> bool:
    return isinstance(obj, str) or (callable(obj) and hasattr(obj, "__name__"))

Try / catch

try:
    resolved = register_tool_ref(fn)
except ValueError as e:
    if "Cannot resolve tool reference" in str(e):
        resolved = register_tool_ref(getattr(fn, "__name__", str(fn)))

Prevention

When it happens

Trigger: Passing an arbitrary object (e.g. a dict, a class instance, a functools.partial-wrapped callable without __name__, or a lambda decorated to strip __name__) to a tool reference/lookup API that calls _resolve_tool_ref.

Common situations: Wrapping functions with decorators (e.g. functools.partial, custom decorators without functools.wraps) so __name__ disappears; passing a Mock or proxy object in tests; passing an already-serialized tool dict instead of its name.

Related errors


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