PrefectHQ/fastmcp · error
To decorate a classmethod, use @classmethod above @tool. See
Error message
To decorate a classmethod, use @classmethod above @tool. See https://gofastmcp.com/servers/tools#using-with-methods
What it means
The @tool decorator rejects classmethod objects directly. Applying @tool below @classmethod wraps the bound classmethod object instead of a function, so the decorator raises TypeError with a pointer to the documented stacking order.
Source
Thrown at fastmcp_slim/fastmcp/tools/function_tool.py:584
Returns the original function with metadata attached. Register with a server
using mcp.add_tool().
Args:
run_in_thread: Applies to sync tool functions only. When True (default),
the sync function is dispatched to a worker thread so it does not
block the event loop. Set to False to run the function inline on the
event loop thread — useful for libraries with thread affinity
(e.g. Windows COM via `uiautomation`/`comtypes`/`pywin32`, `tkinter`,
some GPU/driver bindings). Ignored for async functions. Cannot be
combined with `timeout` on a sync function: inline calls have no
cancellation checkpoints, so the timeout would be a silent no-op.
"""
if isinstance(annotations, dict):
annotations = ToolAnnotations(**annotations)
if isinstance(name_or_fn, classmethod):
raise TypeError(
"To decorate a classmethod, use @classmethod above @tool. "
"See https://gofastmcp.com/servers/tools#using-with-methods"
)
def attach_metadata(fn: F, tool_name: str | None) -> F:
metadata = ToolMeta(
name=tool_name,
version=version,
title=title,
description=description,
icons=icons,
tags=tags,
output_schema=output_schema,
annotations=annotations,
meta=meta,
task=task,
timeout=timeout,
auth=auth,View on GitHub (pinned to 1f02114297)
Solutions
- Reorder the decorators so @classmethod sits above @tool: @classmethod @tool def foo(cls): ...
- Convert the method to a regular method or staticmethod/plain function where appropriate
- Decorate inside the class body after instantiation-based registration is not needed — use the documented method pattern
Example fix
// before @tool @classmethod def my_tool(cls, x: int) -> int: ... // after @classmethod @tool def my_tool(cls, x: int) -> int: ...
Defensive patterns
Strategy: type-guard
Validate before calling
def safe_tool(obj):
import types
if isinstance(obj, classmethod):
raise TypeError('put @classmethod above @tool')
return obj Type guard
def is_classmethod_obj(obj) -> bool:
return isinstance(obj, classmethod) Try / catch
try:
tool = tool_decorator(fn)
except TypeError as e:
if 'classmethod' in str(e):
tool = tool_decorator(classmethod(fn).__func__) # or reorder decorators Prevention
- Always stack @classmethod above @tool in class-based tools
- Favor @staticmethod or plain functions for tools to avoid ordering pitfalls
- Add a unit test that imports the tools module so bad decorator order fails at import
When it happens
Trigger: Writing: @tool @classmethod def foo(cls): ... — i.e. tool decorator applied before (above) classmethod, so name_or_fn is a classmethod instance.
Common situations: Defining tools as class methods on a server class and getting decorator order wrong; copying plain-function tool examples onto class-based code.
Related errors
- To decorate a classmethod, use @classmethod above @prompt. S
- The function '{fn_name}' has '{params[0]}' as its first para
- Cannot specify both a name as first argument and as keyword
- Invalid first argument: {type(name_or_fn)}
- Cannot pass both 'metadata' and individual parameters to fro
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/27b5c8bd97128663.
Report an issue: GitHub.