{"record":{"id":"27b5c8bd97128663","repo":"PrefectHQ/fastmcp","slug":"to-decorate-a-classmethod-use-classmethod-above-27b5c8","errorCode":null,"errorMessage":"To decorate a classmethod, use @classmethod above @tool. See https://gofastmcp.com/servers/tools#using-with-methods","messagePattern":"To decorate a classmethod, use @classmethod above @tool\\. See https://gofastmcp\\.com/servers/tools#using-with-methods","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/tools/function_tool.py","lineNumber":584,"sourceCode":"\n    Returns the original function with metadata attached. Register with a server\n    using mcp.add_tool().\n\n    Args:\n        run_in_thread: Applies to sync tool functions only. When True (default),\n            the sync function is dispatched to a worker thread so it does not\n            block the event loop. Set to False to run the function inline on the\n            event loop thread — useful for libraries with thread affinity\n            (e.g. Windows COM via `uiautomation`/`comtypes`/`pywin32`, `tkinter`,\n            some GPU/driver bindings). Ignored for async functions. Cannot be\n            combined with `timeout` on a sync function: inline calls have no\n            cancellation checkpoints, so the timeout would be a silent no-op.\n    \"\"\"\n    if isinstance(annotations, dict):\n        annotations = ToolAnnotations(**annotations)\n\n    if isinstance(name_or_fn, classmethod):\n        raise TypeError(\n            \"To decorate a classmethod, use @classmethod above @tool. \"\n            \"See https://gofastmcp.com/servers/tools#using-with-methods\"\n        )\n\n    def attach_metadata(fn: F, tool_name: str | None) -> F:\n        metadata = ToolMeta(\n            name=tool_name,\n            version=version,\n            title=title,\n            description=description,\n            icons=icons,\n            tags=tags,\n            output_schema=output_schema,\n            annotations=annotations,\n            meta=meta,\n            task=task,\n            timeout=timeout,\n            auth=auth,","sourceCodeStart":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/tools/function_tool.py#L566-L602","documentation":"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.","triggerScenarios":"Writing:\n@tool\n@classmethod\ndef foo(cls): ... — i.e. tool decorator applied before (above) classmethod, so name_or_fn is a classmethod instance.","commonSituations":"Defining tools as class methods on a server class and getting decorator order wrong; copying plain-function tool examples onto class-based code.","solutions":["Reorder the decorators so @classmethod sits above @tool:\n@classmethod\n@tool\ndef 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"],"exampleFix":"// before\n@tool\n@classmethod\ndef my_tool(cls, x: int) -> int: ...\n// after\n@classmethod\n@tool\ndef my_tool(cls, x: int) -> int: ...","handlingStrategy":"type-guard","validationCode":"def safe_tool(obj):\n    import types\n    if isinstance(obj, classmethod):\n        raise TypeError('put @classmethod above @tool')\n    return obj","typeGuard":"def is_classmethod_obj(obj) -> bool:\n    return isinstance(obj, classmethod)","tryCatchPattern":"try:\n    tool = tool_decorator(fn)\nexcept TypeError as e:\n    if 'classmethod' in str(e):\n        tool = tool_decorator(classmethod(fn).__func__)  # or reorder decorators","preventionTips":["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"],"tags":["python","decorators","classmethod","tool-definition"],"backgroundTag":"decorator-order-mismatch","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}