一、装饰方法
正确使用实例方法、类方法和静态方法与 FastMCP 装饰器。
FastMCP 的装饰器系统设计用于处理函数,但如果尝试装饰实例或类方法,可能会看到意外行为。本指南解释了使用所有 FastMCP 装饰器(@tool、@resource 和 @prompt)处理方法的正确方法。
二、为什么方法难以处理?
当将 FastMCP 装饰器如 @tool、@resource 或 @prompt 应用于方法时,装饰器在装饰时捕获函数。对于实例方法和类方法,这带来了挑战,因为:
对于实例方法:装饰器在任何实例存在之前获取未绑定的方法
对于类方法:装饰器在函数绑定到类之前获取函数
这意味着直接装饰这些方法不会按预期工作。实际上,LLM 会看到像 self 或 cls 这样的参数,但它无法提供这些参数的值。
此外,FastMCP 装饰器返回对象(Tool、Resource 或 Prompt 实例)而不是原始函数。这意味着当直接装饰方法时,方法变成返回的对象,不再能被代码调用:
警告:不要这样做!
方法将无法从 Python 调用,工具也无法被 LLM 调用。
from fastmcp import FastMCP
mcp = FastMCP()
class MyClass:
@mcp.tool
def my_method(self, x: int) -> int:
return x * 2
obj = MyClass()
obj.my_method(5) # 失败 - my_method 是 Tool,不是函数
这是在定义类后以函数方式注册方法的另一个重要原因。
三、推荐模式
3.1 实例方法
警告:不要这样做!
from fastmcp import FastMCP
mcp = FastMCP()
class MyClass:
@mcp.tool # 这不会正确工作
def add(self, x, y):
return x + y
当以这种方式应用装饰器时,它捕获未绑定的方法。当 LLM 稍后尝试使用此组件时,它会将 self 视为必需参数,但不知道为其提供什么,导致错误或意外行为。
正确做法:改为这样做:
from fastmcp import FastMCP
mcp = FastMCP()
class MyClass:
def add(self, x, y):
return x + y
# 先创建实例,然后注册绑定方法
obj = MyClass()
mcp.tool(obj.add)
# 现在可以调用它而不会出现 'self' 作为参数
await mcp._mcp_call_tool('add', {'x': 1, 'y': 2}) # 返回 3
这种方法有效是因为:
首先创建类的实例(obj)
通过实例访问方法时(obj.add),Python 创建绑定方法,其中 self 已设置为该实例
注册此绑定方法时,系统看到只期望适当参数的可调用对象,而不是 self
3.2 类方法
装饰类方法的行为取决于装饰器的顺序:
警告:不要这样做(装饰器顺序很重要):
from fastmcp import FastMCP
mcp = FastMCP()
class MyClass:
@classmethod
@mcp.tool # 这不会工作但不会引发错误
def from_string_v1(cls, s):
return cls(s)
@mcp.tool
@classmethod # 这将引发有用的 ValueError
def from_string_v2(cls, s):
return cls(s)
如果 @classmethod 在前,然后 @mcp.tool:不会引发错误,但不会正确工作
如果 @mcp.tool 在前,然后 @classmethod:FastMCP 将检测到这一点并引发带有指导的有用 ValueError
正确做法:改为这样做:
from fastmcp import FastMCP
mcp = FastMCP()
class MyClass:
@classmethod
def from_string(cls, s):
return cls(s)
# 在类定义后注册类方法
mcp.tool(MyClass.from_string)
这有效是因为:
@classmethod 装饰器在类定义期间正确应用
访问 MyClass.from_string 时,Python 提供特殊的方法对象,自动将类绑定到 cls 参数
注册时,只有适当的参数暴露给 LLM,隐藏了 cls 参数的实现细节
3.3 静态方法
静态方法"可以"与 FastMCP 装饰器一起使用,但不推荐这样做,因为 FastMCP 装饰器不会返回可调用的方法。因此,应该以与其他方法相同的方式注册静态方法。
警告:不推荐这样做,尽管它会工作。
from fastmcp import FastMCP
mcp = FastMCP()
class MyClass:
@mcp.tool
@staticmethod
def utility(x, y):
return x + y
这有效是因为 @staticmethod 将方法转换为常规函数,然后 FastMCP 装饰器可以正确处理。但是,不推荐这样做,因为 FastMCP 装饰器不会返回可调用的静态方法。因此,应该以与其他方法相同的方式注册静态方法。
正确做法:更喜欢这种模式:
from fastmcp import FastMCP
mcp = FastMCP()
class MyClass:
@staticmethod
def utility(x, y):
return x + y
# 这也有效
mcp.tool(MyClass.utility)
四、其他模式
在类初始化时创建组件
可以在创建对象时自动注册实例方法:
from fastmcp import FastMCP
mcp = FastMCP()
class ComponentProvider:
def __init__(self, mcp_instance):
# 注册方法
mcp_instance.tool(self.tool_method)
mcp_instance.resource("resource://data")(self.resource_method)
def tool_method(self, x):
return x * 2
def resource_method(self):
return "资源数据"
# 创建实例时自动注册方法
provider = ComponentProvider(mcp)
这种模式在以下情况下有用:
希望将注册逻辑封装在类本身内
有多个应一起注册的相关组件
希望确保在创建实例时始终正确注册方法
类在初始化期间自动注册其方法,确保它们在注册前正确绑定到实例。
五、总结
FastMCP 装饰器与方法一起使用的当前行为是:
静态方法: 可以直接装饰,并与所有 FastMCP 装饰器完美配合
类方法: 不能直接装饰,将引发带有指导的有用 ValueError
实例方法: 应在创建实例后使用装饰器调用注册
对于类和实例方法,应在创建实例或类后注册它们,以确保正确的方法绑定。这确保方法在注册前正确绑定。
理解这些模式允许有效地将组件组织到类中,同时保持正确的方法绑定,在享受面向对象设计好处的同时不牺牲 FastMCP 装饰器系统的简单性。
浙公网安备 33010602011771号