Jeremiah Lowin commited on
Commit
f581042
·
1 Parent(s): 0feb19f
docs/docs.json CHANGED
@@ -55,10 +55,11 @@
55
  ]
56
  },
57
  {
58
- "group": "Advanced Patterns",
59
  "pages": [
60
  "patterns/proxying",
61
  "patterns/composition",
 
62
  "patterns/openapi",
63
  "patterns/fastapi"
64
  ]
 
55
  ]
56
  },
57
  {
58
+ "group": "Patterns",
59
  "pages": [
60
  "patterns/proxying",
61
  "patterns/composition",
62
+ "patterns/decorating-methods",
63
  "patterns/openapi",
64
  "patterns/fastapi"
65
  ]
docs/patterns/decorating-methods.mdx ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Decorating Methods
3
+ sidebarTitle: Decorating Methods
4
+ description: Properly use instance methods, class methods, and static methods with FastMCP decorators.
5
+ icon: at
6
+ ---
7
+
8
+ FastMCP's decorator system is designed to work with functions, but you may see unexpected behavior if you try to decorate an instance or class method. This guide explains the correct approach for using methods with all FastMCP decorators (`@mcp.tool()`, `@mcp.resource()`, and `@mcp.prompt()`).
9
+
10
+ ## Why Are Methods Hard?
11
+
12
+ When you apply a FastMCP decorator like `@mcp.tool()`, `@mcp.resource()`, or `@mcp.prompt()` to a method, the decorator captures the function at decoration time. For instance methods and class methods, this poses a challenge because:
13
+
14
+ 1. For instance methods: The decorator gets the unbound method before any instance exists
15
+ 2. For class methods: The decorator gets the function before it's bound to the class
16
+
17
+ This means directly decorating these methods doesn't work as expected. In practice, the LLM would see parameters like `self` or `cls` that it cannot provide values for.
18
+
19
+ ## Recommended Patterns
20
+
21
+ ### Instance Methods
22
+
23
+ **Don't do this** (it doesn't work properly):
24
+
25
+ ```python
26
+ from fastmcp import FastMCP
27
+
28
+ mcp = FastMCP()
29
+
30
+ class MyClass:
31
+ @mcp.tool() # This won't work correctly
32
+ def add(self, x, y):
33
+ return x + y
34
+
35
+ @mcp.resource("resource://{param}") # This won't work correctly
36
+ def get_resource(self, param: str):
37
+ return f"Resource data for {param}"
38
+ ```
39
+
40
+ When the decorator is applied this way, it captures the unbound method. When the LLM later tries to use this component, it will see `self` as a required parameter, but it won't know what to provide for it, causing errors or unexpected behavior.
41
+
42
+ **Do this instead**:
43
+
44
+ ```python
45
+ from fastmcp import FastMCP
46
+
47
+ mcp = FastMCP()
48
+
49
+ class MyClass:
50
+ def add(self, x, y):
51
+ return x + y
52
+
53
+ def get_resource(self, param: str):
54
+ return f"Resource data for {param}"
55
+
56
+ # Create an instance first, then add the bound methods
57
+ obj = MyClass()
58
+ mcp.add_tool(obj.add)
59
+ mcp.add_resource(obj.get_resource, uri="resource://{param}") # For resources
60
+
61
+ # Now you can call it without 'self' showing up as a parameter
62
+ await mcp.call_tool('add', {'x': 1, 'y': 2}) # Returns 3
63
+ ```
64
+
65
+ This approach works because:
66
+ 1. You first create an instance of the class (`obj`)
67
+ 2. When you access the method through the instance (`obj.add`), Python creates a bound method where `self` is already set to that instance
68
+ 3. When you register this bound method, the system sees a callable that only expects the appropriate parameters, not `self`
69
+
70
+ ### Class Methods
71
+
72
+ Similar to instance methods, decorating class methods directly doesn't work properly:
73
+
74
+ **Don't do this**:
75
+
76
+ ```python
77
+ from fastmcp import FastMCP
78
+
79
+ mcp = FastMCP()
80
+
81
+ class MyClass:
82
+ @classmethod
83
+ @mcp.tool() # This won't work correctly
84
+ def from_string(cls, s):
85
+ return cls(s)
86
+ ```
87
+
88
+ The problem here is that the FastMCP decorator is applied before the `@classmethod` decorator (Python applies decorators bottom-to-top). So it captures the function before it's transformed into a class method, leading to incorrect behavior.
89
+
90
+ **Do this instead**:
91
+
92
+ ```python
93
+ from fastmcp import FastMCP
94
+
95
+ mcp = FastMCP()
96
+
97
+ class MyClass:
98
+ @classmethod
99
+ def from_string(cls, s):
100
+ return cls(s)
101
+
102
+ # Add the class method after the class is defined
103
+ mcp.add_tool(MyClass.from_string)
104
+ ```
105
+
106
+ This works because:
107
+ 1. The `@classmethod` decorator is applied properly during class definition
108
+ 2. When you access `MyClass.from_string`, Python provides a special method object that automatically binds the class to the `cls` parameter
109
+ 3. When registered, only the appropriate parameters are exposed to the LLM, hiding the implementation detail of the `cls` parameter
110
+
111
+ ### Static Methods
112
+
113
+ Unlike instance and class methods, static methods work fine with FastMCP decorators:
114
+
115
+ ```python
116
+ from fastmcp import FastMCP
117
+
118
+ mcp = FastMCP()
119
+
120
+ class MyClass:
121
+ @staticmethod
122
+ @mcp.tool() # This works!
123
+ def utility(x, y):
124
+ return x + y
125
+
126
+ @staticmethod
127
+ @mcp.resource("resource://data") # This works too!
128
+ def get_data():
129
+ return "Static resource data"
130
+ ```
131
+
132
+ This approach works because:
133
+ 1. The `@staticmethod` decorator is applied first (executed last), transforming the method into a regular function
134
+ 2. When the FastMCP decorator is applied, it's capturing what is effectively just a regular function
135
+ 3. A static method doesn't have any binding requirements - it doesn't receive a `self` or `cls` parameter
136
+
137
+ Alternatively, you can use the same pattern as the other methods:
138
+
139
+ ```python
140
+ from fastmcp import FastMCP
141
+
142
+ mcp = FastMCP()
143
+
144
+ class MyClass:
145
+ @staticmethod
146
+ def utility(x, y):
147
+ return x + y
148
+
149
+ # This also works
150
+ mcp.add_tool(MyClass.utility)
151
+ ```
152
+
153
+ This works for the same reason - a static method is essentially just a function in a class namespace.
154
+
155
+ ## Additional Patterns
156
+
157
+ ### Creating Components at Class Initialization
158
+
159
+ You can automatically register instance methods when creating an object:
160
+
161
+ ```python
162
+ from fastmcp import FastMCP
163
+
164
+ mcp = FastMCP()
165
+
166
+ class ComponentProvider:
167
+ def __init__(self, mcp_instance):
168
+ # Register methods
169
+ mcp_instance.add_tool(self.tool_method)
170
+ mcp_instance.add_resource(self.resource_method, uri="resource://data")
171
+
172
+ def tool_method(self, x):
173
+ return x * 2
174
+
175
+ def resource_method(self):
176
+ return "Resource data"
177
+
178
+ # The methods are automatically registered when creating the instance
179
+ provider = ComponentProvider(mcp)
180
+ ```
181
+
182
+ This pattern is useful when:
183
+ - You want to encapsulate registration logic within the class itself
184
+ - You have multiple related components that should be registered together
185
+ - You want to ensure that methods are always properly registered when creating an instance
186
+
187
+ The class automatically registers its methods during initialization, ensuring they're properly bound to the instance before registration.
188
+
189
+ ## Summary
190
+
191
+ While FastMCP's decorator pattern works seamlessly with regular functions and static methods, for instance methods and class methods, you should add them after creating the instance or class. This ensures that the methods are properly bound before being registered.
192
+
193
+ These patterns apply to all FastMCP decorators and registration methods:
194
+ - `@mcp.tool()` and `mcp.add_tool()`
195
+ - `@mcp.resource()` and `mcp.add_resource()`
196
+ - `@mcp.prompt()` and `mcp.add_prompt()`
197
+
198
+ Understanding these patterns allows you to effectively organize your components into classes while maintaining proper method binding, giving you the benefits of object-oriented design without sacrificing the simplicity of FastMCP's decorator system.