FastMCP的装饰器系统设计用于处理函数,但如果您尝试装饰实例或类方法,可能会遇到意外行为。本指南解释了如何正确使用方法与所有FastMCP装饰器(@tool()@resource()@prompt())。

为什么方法如此困难?

当你应用像@tool()@resource()@prompt()这样的FastMCP装饰器到一个方法时,装饰器会在装饰时捕获该函数。对于实例方法和类方法来说,这带来了一个挑战,因为:

  1. 例如实例方法:装饰器在任何实例存在之前获取未绑定的方法
  2. 对于类方法:装饰器在函数绑定到类之前获取该函数

这意味着直接装饰这些方法不会按预期工作。实际上,LLM会看到像selfcls这样的参数,但它无法为这些参数提供值。

实例方法

不要这样做(它无法正常工作):

from fastmcp import FastMCP

mcp = FastMCP()

class MyClass:
    @mcp.tool()  # This won't work correctly
    def add(self, x, y):
        return x + y
    
    @mcp.resource("resource://{param}")  # This won't work correctly
    def get_resource(self, param: str):
        return f"Resource data for {param}"

当以这种方式应用装饰器时,它会捕获未绑定的方法。当LLM稍后尝试使用此组件时,它会将self视为必需参数,但不知道该为其提供什么值,从而导致错误或意外行为。

请改为这样做:

from fastmcp import FastMCP

mcp = FastMCP()

class MyClass:
    def add(self, x, y):
        return x + y
    
    def get_resource(self, param: str):
        return f"Resource data for {param}"

# Create an instance first, then add the bound methods
obj = MyClass()
mcp.add_tool(obj.add)
mcp.add_resource_fn(obj.get_resource, uri="resource://{param}")  # For resources or templates

# Note: FastMCP provides add_resource() for adding Resource objects directly and
# add_resource_fn() for adding functions that generate resources or templates

# Now you can call it without 'self' showing up as a parameter
await mcp.call_tool('add', {'x': 1, 'y': 2})  # Returns 3

这种方法之所以有效是因为:

  1. 首先创建该类的实例(obj)
  2. 当你通过实例访问该方法时(obj.add),Python会创建一个绑定方法,其中self已经设置为该实例
  3. 当你注册这个绑定方法时,系统会看到一个仅需要适当参数的可调用对象,而不需要self

类方法

与实例方法类似,直接装饰类方法无法正常工作:

请不要这样做:

from fastmcp import FastMCP

mcp = FastMCP()

class MyClass:
    @classmethod
    @mcp.tool()  # This won't work correctly
    def from_string(cls, s):
        return cls(s)

这里的问题在于FastMCP装饰器是在@classmethod装饰器之前应用的(Python的装饰器是从下往上执行的)。因此它捕获的是尚未转换为类方法的函数,从而导致错误行为。

请改为这样做:

from fastmcp import FastMCP

mcp = FastMCP()

class MyClass:
    @classmethod
    def from_string(cls, s):
        return cls(s)

# Add the class method after the class is defined
mcp.add_tool(MyClass.from_string)

这是因为:

  1. 在类定义期间正确应用了@classmethod装饰器
  2. 当你访问MyClass.from_string时,Python会提供一个特殊的方法对象,自动将类绑定到cls参数
  3. 注册时,只有适当的参数会暴露给LLM,隐藏了cls参数的实现细节

静态方法

与实例方法和类方法不同,静态方法可以很好地与FastMCP装饰器配合使用:

from fastmcp import FastMCP

mcp = FastMCP()

class MyClass:
    @staticmethod
    @mcp.tool()  # This works!
    def utility(x, y):
        return x + y
    
    @staticmethod
    @mcp.resource("resource://data")  # This works too!
    def get_data():
        return "Static resource data"

这种方法之所以有效是因为:

  1. 首先应用@staticmethod装饰器(最后执行),将方法转换为常规函数
  2. 当应用FastMCP装饰器时,它捕获的实际上只是一个常规函数
  3. 静态方法没有任何绑定要求 - 它不会接收selfcls参数

或者,您可以使用与其他方法相同的模式:

from fastmcp import FastMCP

mcp = FastMCP()

class MyClass:
    @staticmethod
    def utility(x, y):
        return x + y

# This also works
mcp.add_tool(MyClass.utility)

这之所以有效是基于同样的原因 - 静态方法本质上只是类命名空间中的一个函数。

其他模式

在类初始化时创建组件

在创建对象时,您可以自动注册实例方法:

from fastmcp import FastMCP

mcp = FastMCP()

class ComponentProvider:
    def __init__(self, mcp_instance):
        # Register methods
        mcp_instance.add_tool(self.tool_method)
        mcp_instance.add_resource_fn(self.resource_method, uri="resource://data")
    
    def tool_method(self, x):
        return x * 2
    
    def resource_method(self):
        return "Resource data"

# The methods are automatically registered when creating the instance
provider = ComponentProvider(mcp)

这种模式在以下情况下很有用:

  • 您希望将注册逻辑封装在类本身中
  • 您有多个相关组件需要一起注册
  • 您希望确保在创建实例时方法总是被正确注册

该类在初始化过程中自动注册其方法,确保在注册前它们已正确绑定到实例上。

概述

虽然FastMCP的装饰器模式可以无缝应用于常规函数和静态方法,但对于实例方法和类方法,您应在创建实例或类之后再添加它们。这能确保方法在注册之前已正确绑定。

这些模式适用于所有FastMCP装饰器和注册方法:

  • @tool()add_tool()
  • @resource()add_resource_fn()
  • @prompt()add_prompt()

理解这些模式可以让您有效地将组件组织到类中,同时保持正确的方法绑定,从而在不牺牲FastMCP装饰器系统简洁性的前提下,获得面向对象设计的优势。