{"record":{"id":"2d303b8676e2b776","repo":"PrefectHQ/fastmcp","slug":"to-decorate-a-classmethod-use-classmethod-above-2d303b","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":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/providers/local_provider/decorators/tools.py","lineNumber":272,"sourceCode":"\n        Example:\n            ```python\n            provider = LocalProvider()\n\n            @provider.tool\n            def greet(name: str) -> str:\n                return f\"Hello, {name}!\"\n\n            @provider.tool(\"custom_name\")\n            def my_tool(x: int) -> str:\n                return str(x)\n            ```\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 decorate_and_register(\n            fn: AnyFunction, tool_name: str | None\n        ) -> FunctionTool | AnyFunction:\n            # Check for unbound method\n            try:\n                params = list(inspect.signature(fn).parameters.keys())\n            except (ValueError, TypeError):\n                params = []\n            if params and params[0] in (\"self\", \"cls\"):\n                fn_name = getattr(fn, \"__name__\", \"function\")\n                raise TypeError(\n                    f\"The function '{fn_name}' has '{params[0]}' as its first parameter. \"\n                    f\"Use the standalone @tool decorator and register the bound method:\\n\\n\"\n                    f\"    from fastmcp.tools import tool\\n\\n\"","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/providers/local_provider/decorators/tools.py#L254-L290","documentation":"@tool was applied directly to a classmethod object (i.e., @tool below @classmethod). FastMCP cannot introspect a classmethod wrapper as the tool function, so it raises and instructs to place @classmethod above @tool.","triggerScenarios":"Writing `@tool` above `@classmethod` in a class body, so `tool` receives the classmethod object as name_or_fn.","commonSituations":"Exposing class-level factory methods as tools; copying instance-method tool patterns and adapting to classmethods with the decorators in the wrong order.","solutions":["Swap the decorator order: put @classmethod first, then @tool beneath it.","Convert the classmethod to a staticmethod or module-level function if no cls access is needed.","Decorate with the standalone @tool and register the already-classmethod'd bound method via mcp.add_tool(MyClass.method)."],"exampleFix":"// before\nclass MyClass:\n    @tool\n    @classmethod\n    def create(cls, name: str) -> str:\n        ...\n\n// after\nclass MyClass:\n    @classmethod\n    @tool\n    def create(cls, name: str) -> str:\n        ...","handlingStrategy":"type-guard","validationCode":"import inspect\n\ndef safe_tool(obj):\n    if isinstance(obj, classmethod):\n        raise TypeError('place @classmethod above @tool')\n    return tool(obj)","typeGuard":"def is_plain_callable(obj) -> bool:\n    return inspect.isroutine(obj) and not isinstance(obj, classmethod)","tryCatchPattern":"try:\n    tool_fn = tool(MyClass.create)\nexcept TypeError as e:\n    if 'use @classmethod above @tool' in str(e):\n        raise SyntaxHintError('reorder: @classmethod on top, @tool below') from e\n    raise","preventionTips":["Always order decorators as @classmethod then @tool (top to bottom).","Prefer staticmethod for tools that don't need cls.","Add a unit test that imports the tools module so decorator-order errors surface at test time."],"tags":["python","fastmcp","tools","decorator-order"],"backgroundTag":"decorator-applied-to-method","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}