fastmcp 2.14.4__py3-none-any.whl → 3.0.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. fastmcp/_vendor/__init__.py +1 -0
  2. fastmcp/_vendor/docket_di/README.md +7 -0
  3. fastmcp/_vendor/docket_di/__init__.py +163 -0
  4. fastmcp/cli/cli.py +112 -28
  5. fastmcp/cli/install/claude_code.py +1 -5
  6. fastmcp/cli/install/claude_desktop.py +1 -5
  7. fastmcp/cli/install/cursor.py +1 -5
  8. fastmcp/cli/install/gemini_cli.py +1 -5
  9. fastmcp/cli/install/mcp_json.py +1 -6
  10. fastmcp/cli/run.py +146 -5
  11. fastmcp/client/__init__.py +7 -9
  12. fastmcp/client/auth/oauth.py +18 -17
  13. fastmcp/client/client.py +100 -870
  14. fastmcp/client/elicitation.py +1 -1
  15. fastmcp/client/mixins/__init__.py +13 -0
  16. fastmcp/client/mixins/prompts.py +295 -0
  17. fastmcp/client/mixins/resources.py +325 -0
  18. fastmcp/client/mixins/task_management.py +157 -0
  19. fastmcp/client/mixins/tools.py +397 -0
  20. fastmcp/client/sampling/handlers/anthropic.py +2 -2
  21. fastmcp/client/sampling/handlers/openai.py +1 -1
  22. fastmcp/client/tasks.py +3 -3
  23. fastmcp/client/telemetry.py +47 -0
  24. fastmcp/client/transports/__init__.py +38 -0
  25. fastmcp/client/transports/base.py +82 -0
  26. fastmcp/client/transports/config.py +170 -0
  27. fastmcp/client/transports/http.py +145 -0
  28. fastmcp/client/transports/inference.py +154 -0
  29. fastmcp/client/transports/memory.py +90 -0
  30. fastmcp/client/transports/sse.py +89 -0
  31. fastmcp/client/transports/stdio.py +543 -0
  32. fastmcp/contrib/component_manager/README.md +4 -10
  33. fastmcp/contrib/component_manager/__init__.py +1 -2
  34. fastmcp/contrib/component_manager/component_manager.py +95 -160
  35. fastmcp/contrib/component_manager/example.py +1 -1
  36. fastmcp/contrib/mcp_mixin/example.py +4 -4
  37. fastmcp/contrib/mcp_mixin/mcp_mixin.py +11 -4
  38. fastmcp/decorators.py +41 -0
  39. fastmcp/dependencies.py +12 -1
  40. fastmcp/exceptions.py +4 -0
  41. fastmcp/experimental/server/openapi/__init__.py +18 -15
  42. fastmcp/mcp_config.py +13 -4
  43. fastmcp/prompts/__init__.py +6 -3
  44. fastmcp/prompts/function_prompt.py +465 -0
  45. fastmcp/prompts/prompt.py +321 -271
  46. fastmcp/resources/__init__.py +5 -3
  47. fastmcp/resources/function_resource.py +335 -0
  48. fastmcp/resources/resource.py +325 -115
  49. fastmcp/resources/template.py +215 -43
  50. fastmcp/resources/types.py +27 -12
  51. fastmcp/server/__init__.py +2 -2
  52. fastmcp/server/auth/__init__.py +14 -0
  53. fastmcp/server/auth/auth.py +30 -10
  54. fastmcp/server/auth/authorization.py +190 -0
  55. fastmcp/server/auth/oauth_proxy/__init__.py +14 -0
  56. fastmcp/server/auth/oauth_proxy/consent.py +361 -0
  57. fastmcp/server/auth/oauth_proxy/models.py +178 -0
  58. fastmcp/server/auth/{oauth_proxy.py → oauth_proxy/proxy.py} +24 -778
  59. fastmcp/server/auth/oauth_proxy/ui.py +277 -0
  60. fastmcp/server/auth/oidc_proxy.py +2 -2
  61. fastmcp/server/auth/providers/auth0.py +24 -94
  62. fastmcp/server/auth/providers/aws.py +26 -95
  63. fastmcp/server/auth/providers/azure.py +41 -129
  64. fastmcp/server/auth/providers/descope.py +18 -49
  65. fastmcp/server/auth/providers/discord.py +25 -86
  66. fastmcp/server/auth/providers/github.py +23 -87
  67. fastmcp/server/auth/providers/google.py +24 -87
  68. fastmcp/server/auth/providers/introspection.py +60 -79
  69. fastmcp/server/auth/providers/jwt.py +30 -67
  70. fastmcp/server/auth/providers/oci.py +47 -110
  71. fastmcp/server/auth/providers/scalekit.py +23 -61
  72. fastmcp/server/auth/providers/supabase.py +18 -47
  73. fastmcp/server/auth/providers/workos.py +34 -127
  74. fastmcp/server/context.py +372 -419
  75. fastmcp/server/dependencies.py +541 -251
  76. fastmcp/server/elicitation.py +20 -18
  77. fastmcp/server/event_store.py +3 -3
  78. fastmcp/server/http.py +16 -6
  79. fastmcp/server/lifespan.py +198 -0
  80. fastmcp/server/low_level.py +92 -2
  81. fastmcp/server/middleware/__init__.py +5 -1
  82. fastmcp/server/middleware/authorization.py +312 -0
  83. fastmcp/server/middleware/caching.py +101 -54
  84. fastmcp/server/middleware/middleware.py +6 -9
  85. fastmcp/server/middleware/ping.py +70 -0
  86. fastmcp/server/middleware/tool_injection.py +2 -2
  87. fastmcp/server/mixins/__init__.py +7 -0
  88. fastmcp/server/mixins/lifespan.py +217 -0
  89. fastmcp/server/mixins/mcp_operations.py +392 -0
  90. fastmcp/server/mixins/transport.py +342 -0
  91. fastmcp/server/openapi/__init__.py +41 -21
  92. fastmcp/server/openapi/components.py +16 -339
  93. fastmcp/server/openapi/routing.py +34 -118
  94. fastmcp/server/openapi/server.py +67 -392
  95. fastmcp/server/providers/__init__.py +71 -0
  96. fastmcp/server/providers/aggregate.py +261 -0
  97. fastmcp/server/providers/base.py +578 -0
  98. fastmcp/server/providers/fastmcp_provider.py +674 -0
  99. fastmcp/server/providers/filesystem.py +226 -0
  100. fastmcp/server/providers/filesystem_discovery.py +327 -0
  101. fastmcp/server/providers/local_provider/__init__.py +11 -0
  102. fastmcp/server/providers/local_provider/decorators/__init__.py +15 -0
  103. fastmcp/server/providers/local_provider/decorators/prompts.py +256 -0
  104. fastmcp/server/providers/local_provider/decorators/resources.py +240 -0
  105. fastmcp/server/providers/local_provider/decorators/tools.py +315 -0
  106. fastmcp/server/providers/local_provider/local_provider.py +465 -0
  107. fastmcp/server/providers/openapi/__init__.py +39 -0
  108. fastmcp/server/providers/openapi/components.py +332 -0
  109. fastmcp/server/providers/openapi/provider.py +405 -0
  110. fastmcp/server/providers/openapi/routing.py +109 -0
  111. fastmcp/server/providers/proxy.py +867 -0
  112. fastmcp/server/providers/skills/__init__.py +59 -0
  113. fastmcp/server/providers/skills/_common.py +101 -0
  114. fastmcp/server/providers/skills/claude_provider.py +44 -0
  115. fastmcp/server/providers/skills/directory_provider.py +153 -0
  116. fastmcp/server/providers/skills/skill_provider.py +432 -0
  117. fastmcp/server/providers/skills/vendor_providers.py +142 -0
  118. fastmcp/server/providers/wrapped_provider.py +140 -0
  119. fastmcp/server/proxy.py +34 -700
  120. fastmcp/server/sampling/run.py +341 -2
  121. fastmcp/server/sampling/sampling_tool.py +4 -3
  122. fastmcp/server/server.py +1214 -2171
  123. fastmcp/server/tasks/__init__.py +2 -1
  124. fastmcp/server/tasks/capabilities.py +13 -1
  125. fastmcp/server/tasks/config.py +66 -3
  126. fastmcp/server/tasks/handlers.py +65 -273
  127. fastmcp/server/tasks/keys.py +4 -6
  128. fastmcp/server/tasks/requests.py +474 -0
  129. fastmcp/server/tasks/routing.py +76 -0
  130. fastmcp/server/tasks/subscriptions.py +20 -11
  131. fastmcp/server/telemetry.py +131 -0
  132. fastmcp/server/transforms/__init__.py +244 -0
  133. fastmcp/server/transforms/namespace.py +193 -0
  134. fastmcp/server/transforms/prompts_as_tools.py +175 -0
  135. fastmcp/server/transforms/resources_as_tools.py +190 -0
  136. fastmcp/server/transforms/tool_transform.py +96 -0
  137. fastmcp/server/transforms/version_filter.py +124 -0
  138. fastmcp/server/transforms/visibility.py +526 -0
  139. fastmcp/settings.py +34 -96
  140. fastmcp/telemetry.py +122 -0
  141. fastmcp/tools/__init__.py +10 -3
  142. fastmcp/tools/function_parsing.py +201 -0
  143. fastmcp/tools/function_tool.py +467 -0
  144. fastmcp/tools/tool.py +215 -362
  145. fastmcp/tools/tool_transform.py +38 -21
  146. fastmcp/utilities/async_utils.py +69 -0
  147. fastmcp/utilities/components.py +152 -91
  148. fastmcp/utilities/inspect.py +8 -20
  149. fastmcp/utilities/json_schema.py +12 -5
  150. fastmcp/utilities/json_schema_type.py +17 -15
  151. fastmcp/utilities/lifespan.py +56 -0
  152. fastmcp/utilities/logging.py +12 -4
  153. fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -3
  154. fastmcp/utilities/openapi/parser.py +3 -3
  155. fastmcp/utilities/pagination.py +80 -0
  156. fastmcp/utilities/skills.py +253 -0
  157. fastmcp/utilities/tests.py +0 -16
  158. fastmcp/utilities/timeout.py +47 -0
  159. fastmcp/utilities/types.py +1 -1
  160. fastmcp/utilities/versions.py +285 -0
  161. {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/METADATA +8 -5
  162. fastmcp-3.0.0b1.dist-info/RECORD +228 -0
  163. fastmcp/client/transports.py +0 -1170
  164. fastmcp/contrib/component_manager/component_service.py +0 -209
  165. fastmcp/prompts/prompt_manager.py +0 -117
  166. fastmcp/resources/resource_manager.py +0 -338
  167. fastmcp/server/tasks/converters.py +0 -206
  168. fastmcp/server/tasks/protocol.py +0 -359
  169. fastmcp/tools/tool_manager.py +0 -170
  170. fastmcp/utilities/mcp_config.py +0 -56
  171. fastmcp-2.14.4.dist-info/RECORD +0 -161
  172. /fastmcp/server/{openapi → providers/openapi}/README.md +0 -0
  173. {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/WHEEL +0 -0
  174. {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/entry_points.txt +0 -0
  175. {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,256 @@
1
+ """Prompt decorator mixin for LocalProvider.
2
+
3
+ This module provides the PromptDecoratorMixin class that adds prompt
4
+ registration functionality to LocalProvider.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import inspect
10
+ from collections.abc import Callable
11
+ from functools import partial
12
+ from typing import TYPE_CHECKING, Any, overload
13
+
14
+ import mcp.types
15
+ from mcp.types import AnyFunction
16
+
17
+ import fastmcp
18
+ from fastmcp.prompts.function_prompt import FunctionPrompt
19
+ from fastmcp.prompts.prompt import Prompt
20
+ from fastmcp.server.tasks.config import TaskConfig
21
+ from fastmcp.tools.tool import AuthCheckCallable
22
+
23
+ if TYPE_CHECKING:
24
+ from fastmcp.server.providers.local_provider import LocalProvider
25
+
26
+
27
+ class PromptDecoratorMixin:
28
+ """Mixin class providing prompt decorator functionality for LocalProvider.
29
+
30
+ This mixin contains all methods related to:
31
+ - Prompt registration via add_prompt()
32
+ - Prompt decorator (@provider.prompt)
33
+ """
34
+
35
+ def add_prompt(self: LocalProvider, prompt: Prompt | Callable[..., Any]) -> Prompt:
36
+ """Add a prompt to this provider's storage.
37
+
38
+ Accepts either a Prompt object or a decorated function with __fastmcp__ metadata.
39
+ """
40
+ enabled = True
41
+ if not isinstance(prompt, Prompt):
42
+ from fastmcp.decorators import get_fastmcp_meta
43
+ from fastmcp.prompts.function_prompt import PromptMeta
44
+
45
+ meta = get_fastmcp_meta(prompt)
46
+ if meta is not None and isinstance(meta, PromptMeta):
47
+ resolved_task = meta.task if meta.task is not None else False
48
+ enabled = meta.enabled
49
+ prompt = Prompt.from_function(
50
+ prompt,
51
+ name=meta.name,
52
+ version=meta.version,
53
+ title=meta.title,
54
+ description=meta.description,
55
+ icons=meta.icons,
56
+ tags=meta.tags,
57
+ meta=meta.meta,
58
+ task=resolved_task,
59
+ auth=meta.auth,
60
+ )
61
+ else:
62
+ raise TypeError(
63
+ f"Expected Prompt or @prompt-decorated function, got {type(prompt).__name__}. "
64
+ "Use @prompt decorator or pass a Prompt instance."
65
+ )
66
+ self._add_component(prompt)
67
+ if not enabled:
68
+ self.disable(keys={prompt.key})
69
+ return prompt
70
+
71
+ @overload
72
+ def prompt(
73
+ self: LocalProvider,
74
+ name_or_fn: AnyFunction,
75
+ *,
76
+ name: str | None = None,
77
+ version: str | int | None = None,
78
+ title: str | None = None,
79
+ description: str | None = None,
80
+ icons: list[mcp.types.Icon] | None = None,
81
+ tags: set[str] | None = None,
82
+ enabled: bool = True,
83
+ meta: dict[str, Any] | None = None,
84
+ task: bool | TaskConfig | None = None,
85
+ auth: AuthCheckCallable | list[AuthCheckCallable] | None = None,
86
+ ) -> FunctionPrompt: ...
87
+
88
+ @overload
89
+ def prompt(
90
+ self: LocalProvider,
91
+ name_or_fn: str | None = None,
92
+ *,
93
+ name: str | None = None,
94
+ version: str | int | None = None,
95
+ title: str | None = None,
96
+ description: str | None = None,
97
+ icons: list[mcp.types.Icon] | None = None,
98
+ tags: set[str] | None = None,
99
+ enabled: bool = True,
100
+ meta: dict[str, Any] | None = None,
101
+ task: bool | TaskConfig | None = None,
102
+ auth: AuthCheckCallable | list[AuthCheckCallable] | None = None,
103
+ ) -> Callable[[AnyFunction], FunctionPrompt]: ...
104
+
105
+ def prompt(
106
+ self: LocalProvider,
107
+ name_or_fn: str | AnyFunction | None = None,
108
+ *,
109
+ name: str | None = None,
110
+ version: str | int | None = None,
111
+ title: str | None = None,
112
+ description: str | None = None,
113
+ icons: list[mcp.types.Icon] | None = None,
114
+ tags: set[str] | None = None,
115
+ enabled: bool = True,
116
+ meta: dict[str, Any] | None = None,
117
+ task: bool | TaskConfig | None = None,
118
+ auth: AuthCheckCallable | list[AuthCheckCallable] | None = None,
119
+ ) -> (
120
+ Callable[[AnyFunction], FunctionPrompt]
121
+ | FunctionPrompt
122
+ | partial[Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt]
123
+ ):
124
+ """Decorator to register a prompt.
125
+
126
+ This decorator supports multiple calling patterns:
127
+ - @provider.prompt (without parentheses)
128
+ - @provider.prompt() (with empty parentheses)
129
+ - @provider.prompt("custom_name") (with name as first argument)
130
+ - @provider.prompt(name="custom_name") (with name as keyword argument)
131
+ - provider.prompt(function, name="custom_name") (direct function call)
132
+
133
+ Args:
134
+ name_or_fn: Either a function (when used as @prompt), a string name, or None
135
+ name: Optional name for the prompt (keyword-only, alternative to name_or_fn)
136
+ title: Optional title for the prompt
137
+ description: Optional description of what the prompt does
138
+ icons: Optional icons for the prompt
139
+ tags: Optional set of tags for categorizing the prompt
140
+ enabled: Whether the prompt is enabled (default True). If False, adds to blocklist.
141
+ meta: Optional meta information about the prompt
142
+ task: Optional task configuration for background execution
143
+ auth: Optional authorization checks for the prompt
144
+
145
+ Returns:
146
+ The registered FunctionPrompt or a decorator function.
147
+
148
+ Example:
149
+ ```python
150
+ provider = LocalProvider()
151
+
152
+ @provider.prompt
153
+ def analyze(topic: str) -> list:
154
+ return [{"role": "user", "content": f"Analyze: {topic}"}]
155
+
156
+ @provider.prompt("custom_name")
157
+ def my_prompt(data: str) -> list:
158
+ return [{"role": "user", "content": data}]
159
+ ```
160
+ """
161
+ if isinstance(name_or_fn, classmethod):
162
+ raise TypeError(
163
+ "To decorate a classmethod, use @classmethod above @prompt. "
164
+ "See https://gofastmcp.com/servers/prompts#using-with-methods"
165
+ )
166
+
167
+ def decorate_and_register(
168
+ fn: AnyFunction, prompt_name: str | None
169
+ ) -> FunctionPrompt | AnyFunction:
170
+ # Check for unbound method
171
+ try:
172
+ params = list(inspect.signature(fn).parameters.keys())
173
+ except (ValueError, TypeError):
174
+ params = []
175
+ if params and params[0] in ("self", "cls"):
176
+ fn_name = getattr(fn, "__name__", "function")
177
+ raise TypeError(
178
+ f"The function '{fn_name}' has '{params[0]}' as its first parameter. "
179
+ f"Use the standalone @prompt decorator and register the bound method:\n\n"
180
+ f" from fastmcp.prompts import prompt\n\n"
181
+ f" class MyClass:\n"
182
+ f" @prompt\n"
183
+ f" def {fn_name}(...):\n"
184
+ f" ...\n\n"
185
+ f" obj = MyClass()\n"
186
+ f" mcp.add_prompt(obj.{fn_name})\n\n"
187
+ f"See https://gofastmcp.com/servers/prompts#using-with-methods"
188
+ )
189
+
190
+ resolved_task: bool | TaskConfig = task if task is not None else False
191
+
192
+ if fastmcp.settings.decorator_mode == "object":
193
+ prompt_obj = Prompt.from_function(
194
+ fn,
195
+ name=prompt_name,
196
+ version=version,
197
+ title=title,
198
+ description=description,
199
+ icons=icons,
200
+ tags=tags,
201
+ meta=meta,
202
+ task=resolved_task,
203
+ auth=auth,
204
+ )
205
+ self._add_component(prompt_obj)
206
+ if not enabled:
207
+ self.disable(keys={prompt_obj.key})
208
+ return prompt_obj
209
+ else:
210
+ from fastmcp.prompts.function_prompt import PromptMeta
211
+
212
+ metadata = PromptMeta(
213
+ name=prompt_name,
214
+ version=version,
215
+ title=title,
216
+ description=description,
217
+ icons=icons,
218
+ tags=tags,
219
+ meta=meta,
220
+ task=task,
221
+ auth=auth,
222
+ enabled=enabled,
223
+ )
224
+ target = fn.__func__ if hasattr(fn, "__func__") else fn
225
+ target.__fastmcp__ = metadata # type: ignore[attr-defined]
226
+ self.add_prompt(fn)
227
+ return fn
228
+
229
+ if inspect.isroutine(name_or_fn):
230
+ return decorate_and_register(name_or_fn, name)
231
+
232
+ elif isinstance(name_or_fn, str):
233
+ if name is not None:
234
+ raise TypeError(
235
+ f"Cannot specify both a name as first argument and as keyword argument. "
236
+ f"Use either @prompt('{name_or_fn}') or @prompt(name='{name}'), not both."
237
+ )
238
+ prompt_name = name_or_fn
239
+ elif name_or_fn is None:
240
+ prompt_name = name
241
+ else:
242
+ raise TypeError(f"Invalid first argument: {type(name_or_fn)}")
243
+
244
+ return partial(
245
+ self.prompt,
246
+ name=prompt_name,
247
+ version=version,
248
+ title=title,
249
+ description=description,
250
+ icons=icons,
251
+ tags=tags,
252
+ meta=meta,
253
+ enabled=enabled,
254
+ task=task,
255
+ auth=auth,
256
+ )
@@ -0,0 +1,240 @@
1
+ """Resource decorator mixin for LocalProvider.
2
+
3
+ This module provides the ResourceDecoratorMixin class that adds resource
4
+ and template registration functionality to LocalProvider.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import inspect
10
+ from collections.abc import Callable
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ import mcp.types
14
+ from mcp.types import Annotations, AnyFunction
15
+
16
+ import fastmcp
17
+ from fastmcp.resources.function_resource import resource as standalone_resource
18
+ from fastmcp.resources.resource import Resource
19
+ from fastmcp.resources.template import ResourceTemplate
20
+ from fastmcp.server.tasks.config import TaskConfig
21
+ from fastmcp.tools.tool import AuthCheckCallable
22
+
23
+ if TYPE_CHECKING:
24
+ from fastmcp.server.providers.local_provider import LocalProvider
25
+
26
+
27
+ class ResourceDecoratorMixin:
28
+ """Mixin class providing resource decorator functionality for LocalProvider.
29
+
30
+ This mixin contains all methods related to:
31
+ - Resource registration via add_resource()
32
+ - Resource template registration via add_template()
33
+ - Resource decorator (@provider.resource)
34
+ """
35
+
36
+ def add_resource(
37
+ self: LocalProvider, resource: Resource | ResourceTemplate | Callable[..., Any]
38
+ ) -> Resource | ResourceTemplate:
39
+ """Add a resource to this provider's storage.
40
+
41
+ Accepts either a Resource/ResourceTemplate object or a decorated function with __fastmcp__ metadata.
42
+ """
43
+ enabled = True
44
+ if not isinstance(resource, (Resource, ResourceTemplate)):
45
+ from fastmcp.decorators import get_fastmcp_meta
46
+ from fastmcp.resources.function_resource import ResourceMeta
47
+ from fastmcp.server.dependencies import without_injected_parameters
48
+
49
+ meta = get_fastmcp_meta(resource)
50
+ if meta is not None and isinstance(meta, ResourceMeta):
51
+ resolved_task = meta.task if meta.task is not None else False
52
+ enabled = meta.enabled
53
+ has_uri_params = "{" in meta.uri and "}" in meta.uri
54
+ wrapper_fn = without_injected_parameters(resource)
55
+ has_func_params = bool(inspect.signature(wrapper_fn).parameters)
56
+
57
+ if has_uri_params or has_func_params:
58
+ resource = ResourceTemplate.from_function(
59
+ fn=resource,
60
+ uri_template=meta.uri,
61
+ name=meta.name,
62
+ version=meta.version,
63
+ title=meta.title,
64
+ description=meta.description,
65
+ icons=meta.icons,
66
+ mime_type=meta.mime_type,
67
+ tags=meta.tags,
68
+ annotations=meta.annotations,
69
+ meta=meta.meta,
70
+ task=resolved_task,
71
+ auth=meta.auth,
72
+ )
73
+ else:
74
+ resource = Resource.from_function(
75
+ fn=resource,
76
+ uri=meta.uri,
77
+ name=meta.name,
78
+ version=meta.version,
79
+ title=meta.title,
80
+ description=meta.description,
81
+ icons=meta.icons,
82
+ mime_type=meta.mime_type,
83
+ tags=meta.tags,
84
+ annotations=meta.annotations,
85
+ meta=meta.meta,
86
+ task=resolved_task,
87
+ auth=meta.auth,
88
+ )
89
+ else:
90
+ raise TypeError(
91
+ f"Expected Resource, ResourceTemplate, or @resource-decorated function, got {type(resource).__name__}. "
92
+ "Use @resource('uri') decorator or pass a Resource/ResourceTemplate instance."
93
+ )
94
+ self._add_component(resource)
95
+ if not enabled:
96
+ self.disable(keys={resource.key})
97
+ return resource
98
+
99
+ def add_template(
100
+ self: LocalProvider, template: ResourceTemplate
101
+ ) -> ResourceTemplate:
102
+ """Add a resource template to this provider's storage."""
103
+ return self._add_component(template)
104
+
105
+ def resource(
106
+ self: LocalProvider,
107
+ uri: str,
108
+ *,
109
+ name: str | None = None,
110
+ version: str | int | None = None,
111
+ title: str | None = None,
112
+ description: str | None = None,
113
+ icons: list[mcp.types.Icon] | None = None,
114
+ mime_type: str | None = None,
115
+ tags: set[str] | None = None,
116
+ enabled: bool = True,
117
+ annotations: Annotations | dict[str, Any] | None = None,
118
+ meta: dict[str, Any] | None = None,
119
+ task: bool | TaskConfig | None = None,
120
+ auth: AuthCheckCallable | list[AuthCheckCallable] | None = None,
121
+ ) -> Callable[[AnyFunction], Resource | ResourceTemplate | AnyFunction]:
122
+ """Decorator to register a function as a resource.
123
+
124
+ If the URI contains parameters (e.g. "resource://{param}") or the function
125
+ has parameters, it will be registered as a template resource.
126
+
127
+ Args:
128
+ uri: URI for the resource (e.g. "resource://my-resource" or "resource://{param}")
129
+ name: Optional name for the resource
130
+ title: Optional title for the resource
131
+ description: Optional description of the resource
132
+ icons: Optional icons for the resource
133
+ mime_type: Optional MIME type for the resource
134
+ tags: Optional set of tags for categorizing the resource
135
+ enabled: Whether the resource is enabled (default True). If False, adds to blocklist.
136
+ annotations: Optional annotations about the resource's behavior
137
+ meta: Optional meta information about the resource
138
+ task: Optional task configuration for background execution
139
+ auth: Optional authorization checks for the resource
140
+
141
+ Returns:
142
+ A decorator function.
143
+
144
+ Example:
145
+ ```python
146
+ provider = LocalProvider()
147
+
148
+ @provider.resource("data://config")
149
+ def get_config() -> str:
150
+ return '{"setting": "value"}'
151
+
152
+ @provider.resource("data://{city}/weather")
153
+ def get_weather(city: str) -> str:
154
+ return f"Weather for {city}"
155
+ ```
156
+ """
157
+ if isinstance(annotations, dict):
158
+ annotations = Annotations(**annotations)
159
+
160
+ if inspect.isroutine(uri):
161
+ raise TypeError(
162
+ "The @resource decorator was used incorrectly. "
163
+ "It requires a URI as the first argument. "
164
+ "Use @resource('uri') instead of @resource"
165
+ )
166
+
167
+ resolved_task: bool | TaskConfig = task if task is not None else False
168
+
169
+ def decorator(fn: AnyFunction) -> Resource | ResourceTemplate | AnyFunction:
170
+ # Check for unbound method
171
+ try:
172
+ params = list(inspect.signature(fn).parameters.keys())
173
+ except (ValueError, TypeError):
174
+ params = []
175
+ if params and params[0] in ("self", "cls"):
176
+ fn_name = getattr(fn, "__name__", "function")
177
+ raise TypeError(
178
+ f"The function '{fn_name}' has '{params[0]}' as its first parameter. "
179
+ f"Use the standalone @resource decorator and register the bound method:\n\n"
180
+ f" from fastmcp.resources import resource\n\n"
181
+ f" class MyClass:\n"
182
+ f" @resource('{uri}')\n"
183
+ f" def {fn_name}(...):\n"
184
+ f" ...\n\n"
185
+ f" obj = MyClass()\n"
186
+ f" mcp.add_resource(obj.{fn_name})\n\n"
187
+ f"See https://gofastmcp.com/servers/resources#using-with-methods"
188
+ )
189
+
190
+ if fastmcp.settings.decorator_mode == "object":
191
+ create_resource = standalone_resource(
192
+ uri,
193
+ name=name,
194
+ version=version,
195
+ title=title,
196
+ description=description,
197
+ icons=icons,
198
+ mime_type=mime_type,
199
+ tags=tags,
200
+ annotations=annotations,
201
+ meta=meta,
202
+ task=resolved_task,
203
+ auth=auth,
204
+ )
205
+ obj = create_resource(fn)
206
+ # In legacy mode, standalone_resource always returns a component
207
+ assert isinstance(obj, (Resource, ResourceTemplate))
208
+ if isinstance(obj, ResourceTemplate):
209
+ self.add_template(obj)
210
+ if not enabled:
211
+ self.disable(keys={obj.key})
212
+ else:
213
+ self.add_resource(obj)
214
+ if not enabled:
215
+ self.disable(keys={obj.key})
216
+ return obj
217
+ else:
218
+ from fastmcp.resources.function_resource import ResourceMeta
219
+
220
+ metadata = ResourceMeta(
221
+ uri=uri,
222
+ name=name,
223
+ version=version,
224
+ title=title,
225
+ description=description,
226
+ icons=icons,
227
+ tags=tags,
228
+ mime_type=mime_type,
229
+ annotations=annotations,
230
+ meta=meta,
231
+ task=task,
232
+ auth=auth,
233
+ enabled=enabled,
234
+ )
235
+ target = fn.__func__ if hasattr(fn, "__func__") else fn
236
+ target.__fastmcp__ = metadata # type: ignore[attr-defined]
237
+ self.add_resource(fn)
238
+ return fn
239
+
240
+ return decorator