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,465 @@
1
+ """LocalProvider for locally-defined MCP components.
2
+
3
+ This module provides the `LocalProvider` class that manages tools, resources,
4
+ templates, and prompts registered via decorators or direct methods.
5
+
6
+ LocalProvider can be used standalone and attached to multiple servers:
7
+
8
+ ```python
9
+ from fastmcp.server.providers import LocalProvider
10
+
11
+ # Create a reusable provider with tools
12
+ provider = LocalProvider()
13
+
14
+ @provider.tool
15
+ def greet(name: str) -> str:
16
+ return f"Hello, {name}!"
17
+
18
+ # Attach to any server
19
+ from fastmcp import FastMCP
20
+ server1 = FastMCP("Server1", providers=[provider])
21
+ server2 = FastMCP("Server2", providers=[provider])
22
+ ```
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ from collections.abc import Sequence
28
+ from typing import Literal, TypeVar
29
+
30
+ from fastmcp.prompts.prompt import Prompt
31
+ from fastmcp.resources.resource import Resource
32
+ from fastmcp.resources.template import ResourceTemplate
33
+ from fastmcp.server.providers.base import Provider
34
+ from fastmcp.server.providers.local_provider.decorators import (
35
+ PromptDecoratorMixin,
36
+ ResourceDecoratorMixin,
37
+ ToolDecoratorMixin,
38
+ )
39
+ from fastmcp.tools.tool import Tool
40
+ from fastmcp.utilities.components import FastMCPComponent
41
+ from fastmcp.utilities.logging import get_logger
42
+ from fastmcp.utilities.versions import VersionSpec, version_sort_key
43
+
44
+ logger = get_logger(__name__)
45
+
46
+ DuplicateBehavior = Literal["error", "warn", "replace", "ignore"]
47
+
48
+ _C = TypeVar("_C", bound=FastMCPComponent)
49
+
50
+
51
+ class LocalProvider(
52
+ Provider,
53
+ ToolDecoratorMixin,
54
+ ResourceDecoratorMixin,
55
+ PromptDecoratorMixin,
56
+ ):
57
+ """Provider for locally-defined components.
58
+
59
+ Supports decorator-based registration (`@provider.tool`, `@provider.resource`,
60
+ `@provider.prompt`) and direct object registration methods.
61
+
62
+ When used standalone, LocalProvider uses default settings. When attached
63
+ to a FastMCP server via the server's decorators, server-level settings
64
+ like `_tool_serializer` and `_support_tasks_by_default` are injected.
65
+
66
+ Example:
67
+ ```python
68
+ from fastmcp.server.providers import LocalProvider
69
+
70
+ # Standalone usage
71
+ provider = LocalProvider()
72
+
73
+ @provider.tool
74
+ def greet(name: str) -> str:
75
+ return f"Hello, {name}!"
76
+
77
+ @provider.resource("data://config")
78
+ def get_config() -> str:
79
+ return '{"setting": "value"}'
80
+
81
+ @provider.prompt
82
+ def analyze(topic: str) -> list:
83
+ return [{"role": "user", "content": f"Analyze: {topic}"}]
84
+
85
+ # Attach to server(s)
86
+ from fastmcp import FastMCP
87
+ server = FastMCP("MyServer", providers=[provider])
88
+ ```
89
+ """
90
+
91
+ def __init__(
92
+ self,
93
+ on_duplicate: DuplicateBehavior = "error",
94
+ ) -> None:
95
+ """Initialize a LocalProvider with empty storage.
96
+
97
+ Args:
98
+ on_duplicate: Behavior when adding a component that already exists:
99
+ - "error": Raise ValueError
100
+ - "warn": Log warning and replace
101
+ - "replace": Silently replace
102
+ - "ignore": Keep existing, return it
103
+ """
104
+ super().__init__()
105
+ self._on_duplicate = on_duplicate
106
+ # Unified component storage - keyed by prefixed key (e.g., "tool:name", "resource:uri")
107
+ self._components: dict[str, FastMCPComponent] = {}
108
+
109
+ # =========================================================================
110
+ # Storage methods
111
+ # =========================================================================
112
+
113
+ def _get_component_identity(self, component: FastMCPComponent) -> tuple[type, str]:
114
+ """Get the identity (type, name/uri) for a component.
115
+
116
+ Returns:
117
+ A tuple of (component_type, logical_name) where logical_name is
118
+ the name for tools/prompts or URI for resources/templates.
119
+ """
120
+ if isinstance(component, Tool):
121
+ return (Tool, component.name)
122
+ elif isinstance(component, ResourceTemplate):
123
+ return (ResourceTemplate, component.uri_template)
124
+ elif isinstance(component, Resource):
125
+ return (Resource, str(component.uri))
126
+ elif isinstance(component, Prompt):
127
+ return (Prompt, component.name)
128
+ else:
129
+ # Fall back to key without version suffix
130
+ key = component.key
131
+ base_key = key.rsplit("@", 1)[0] if "@" in key else key
132
+ return (type(component), base_key)
133
+
134
+ def _check_version_mixing(self, component: _C) -> None:
135
+ """Check that versioned and unversioned components aren't mixed.
136
+
137
+ LocalProvider enforces a simple rule: for any given name/URI, all
138
+ registered components must either be versioned or unversioned, not both.
139
+ This prevents confusing situations where unversioned components can't
140
+ be filtered out by version filters.
141
+
142
+ Args:
143
+ component: The component being added.
144
+
145
+ Raises:
146
+ ValueError: If adding would mix versioned and unversioned components.
147
+ """
148
+ comp_type, logical_name = self._get_component_identity(component)
149
+ is_versioned = component.version is not None
150
+
151
+ # Check all existing components of the same type and logical name
152
+ for existing in self._components.values():
153
+ if not isinstance(existing, comp_type):
154
+ continue
155
+
156
+ _, existing_name = self._get_component_identity(existing)
157
+ if existing_name != logical_name:
158
+ continue
159
+
160
+ existing_versioned = existing.version is not None
161
+ if is_versioned != existing_versioned:
162
+ type_name = comp_type.__name__.lower()
163
+ if is_versioned:
164
+ raise ValueError(
165
+ f"Cannot add versioned {type_name} {logical_name!r} "
166
+ f"(version={component.version!r}): an unversioned "
167
+ f"{type_name} with this name already exists. "
168
+ f"Either version all components or none."
169
+ )
170
+ else:
171
+ raise ValueError(
172
+ f"Cannot add unversioned {type_name} {logical_name!r}: "
173
+ f"versioned {type_name}s with this name already exist "
174
+ f"(e.g., version={existing.version!r}). "
175
+ f"Either version all components or none."
176
+ )
177
+
178
+ def _add_component(self, component: _C) -> _C:
179
+ """Add a component to unified storage.
180
+
181
+ Args:
182
+ component: The component to add.
183
+
184
+ Returns:
185
+ The component that was added (or existing if on_duplicate="ignore").
186
+ """
187
+ existing = self._components.get(component.key)
188
+ if existing:
189
+ if self._on_duplicate == "error":
190
+ raise ValueError(f"Component already exists: {component.key}")
191
+ elif self._on_duplicate == "warn":
192
+ logger.warning(f"Component already exists: {component.key}")
193
+ elif self._on_duplicate == "ignore":
194
+ return existing # type: ignore[return-value]
195
+ # "replace" and "warn" fall through to add
196
+
197
+ # Check for versioned/unversioned mixing before adding
198
+ self._check_version_mixing(component)
199
+
200
+ self._components[component.key] = component
201
+ return component
202
+
203
+ def _remove_component(self, key: str) -> None:
204
+ """Remove a component from unified storage.
205
+
206
+ Args:
207
+ key: The prefixed key of the component.
208
+
209
+ Raises:
210
+ KeyError: If the component is not found.
211
+ """
212
+ component = self._components.get(key)
213
+ if component is None:
214
+ raise KeyError(f"Component {key!r} not found")
215
+
216
+ del self._components[key]
217
+
218
+ def _get_component(self, key: str) -> FastMCPComponent | None:
219
+ """Get a component by its prefixed key.
220
+
221
+ Args:
222
+ key: The prefixed key (e.g., "tool:name", "resource:uri").
223
+
224
+ Returns:
225
+ The component, or None if not found.
226
+ """
227
+ return self._components.get(key)
228
+
229
+ def remove_tool(self, name: str, version: str | None = None) -> None:
230
+ """Remove tool(s) from this provider's storage.
231
+
232
+ Args:
233
+ name: The tool name.
234
+ version: If None, removes ALL versions. If specified, removes only that version.
235
+
236
+ Raises:
237
+ KeyError: If no matching tool is found.
238
+ """
239
+ if version is None:
240
+ # Remove all versions
241
+ keys_to_remove = [
242
+ k
243
+ for k, c in self._components.items()
244
+ if isinstance(c, Tool) and c.name == name
245
+ ]
246
+ if not keys_to_remove:
247
+ raise KeyError(f"Tool {name!r} not found")
248
+ for key in keys_to_remove:
249
+ self._remove_component(key)
250
+ else:
251
+ # Remove specific version - key format is "tool:name@version"
252
+ key = f"{Tool.make_key(name)}@{version}"
253
+ if key not in self._components:
254
+ raise KeyError(f"Tool {name!r} version {version!r} not found")
255
+ self._remove_component(key)
256
+
257
+ def remove_resource(self, uri: str, version: str | None = None) -> None:
258
+ """Remove resource(s) from this provider's storage.
259
+
260
+ Args:
261
+ uri: The resource URI.
262
+ version: If None, removes ALL versions. If specified, removes only that version.
263
+
264
+ Raises:
265
+ KeyError: If no matching resource is found.
266
+ """
267
+ if version is None:
268
+ # Remove all versions
269
+ keys_to_remove = [
270
+ k
271
+ for k, c in self._components.items()
272
+ if isinstance(c, Resource) and str(c.uri) == uri
273
+ ]
274
+ if not keys_to_remove:
275
+ raise KeyError(f"Resource {uri!r} not found")
276
+ for key in keys_to_remove:
277
+ self._remove_component(key)
278
+ else:
279
+ # Remove specific version
280
+ key = f"{Resource.make_key(uri)}@{version}"
281
+ if key not in self._components:
282
+ raise KeyError(f"Resource {uri!r} version {version!r} not found")
283
+ self._remove_component(key)
284
+
285
+ def remove_template(self, uri_template: str, version: str | None = None) -> None:
286
+ """Remove resource template(s) from this provider's storage.
287
+
288
+ Args:
289
+ uri_template: The template URI pattern.
290
+ version: If None, removes ALL versions. If specified, removes only that version.
291
+
292
+ Raises:
293
+ KeyError: If no matching template is found.
294
+ """
295
+ if version is None:
296
+ # Remove all versions
297
+ keys_to_remove = [
298
+ k
299
+ for k, c in self._components.items()
300
+ if isinstance(c, ResourceTemplate) and c.uri_template == uri_template
301
+ ]
302
+ if not keys_to_remove:
303
+ raise KeyError(f"Template {uri_template!r} not found")
304
+ for key in keys_to_remove:
305
+ self._remove_component(key)
306
+ else:
307
+ # Remove specific version
308
+ key = f"{ResourceTemplate.make_key(uri_template)}@{version}"
309
+ if key not in self._components:
310
+ raise KeyError(
311
+ f"Template {uri_template!r} version {version!r} not found"
312
+ )
313
+ self._remove_component(key)
314
+
315
+ def remove_prompt(self, name: str, version: str | None = None) -> None:
316
+ """Remove prompt(s) from this provider's storage.
317
+
318
+ Args:
319
+ name: The prompt name.
320
+ version: If None, removes ALL versions. If specified, removes only that version.
321
+
322
+ Raises:
323
+ KeyError: If no matching prompt is found.
324
+ """
325
+ if version is None:
326
+ # Remove all versions
327
+ keys_to_remove = [
328
+ k
329
+ for k, c in self._components.items()
330
+ if isinstance(c, Prompt) and c.name == name
331
+ ]
332
+ if not keys_to_remove:
333
+ raise KeyError(f"Prompt {name!r} not found")
334
+ for key in keys_to_remove:
335
+ self._remove_component(key)
336
+ else:
337
+ # Remove specific version
338
+ key = f"{Prompt.make_key(name)}@{version}"
339
+ if key not in self._components:
340
+ raise KeyError(f"Prompt {name!r} version {version!r} not found")
341
+ self._remove_component(key)
342
+
343
+ # =========================================================================
344
+ # Provider interface implementation
345
+ # =========================================================================
346
+
347
+ async def _list_tools(self) -> Sequence[Tool]:
348
+ """Return all tools."""
349
+ return [v for v in self._components.values() if isinstance(v, Tool)]
350
+
351
+ async def _get_tool(
352
+ self, name: str, version: VersionSpec | None = None
353
+ ) -> Tool | None:
354
+ """Get a tool by name.
355
+
356
+ Args:
357
+ name: The tool name.
358
+ version: Optional version filter. If None, returns highest version.
359
+ """
360
+ matching = [
361
+ v
362
+ for v in self._components.values()
363
+ if isinstance(v, Tool) and v.name == name
364
+ ]
365
+ if version:
366
+ matching = [t for t in matching if version.matches(t.version)]
367
+ if not matching:
368
+ return None
369
+ return max(matching, key=version_sort_key) # type: ignore[type-var]
370
+
371
+ async def _list_resources(self) -> Sequence[Resource]:
372
+ """Return all resources."""
373
+ return [v for v in self._components.values() if isinstance(v, Resource)]
374
+
375
+ async def _get_resource(
376
+ self, uri: str, version: VersionSpec | None = None
377
+ ) -> Resource | None:
378
+ """Get a resource by URI.
379
+
380
+ Args:
381
+ uri: The resource URI.
382
+ version: Optional version filter. If None, returns highest version.
383
+ """
384
+ matching = [
385
+ v
386
+ for v in self._components.values()
387
+ if isinstance(v, Resource) and str(v.uri) == uri
388
+ ]
389
+ if version:
390
+ matching = [r for r in matching if version.matches(r.version)]
391
+ if not matching:
392
+ return None
393
+ return max(matching, key=version_sort_key) # type: ignore[type-var]
394
+
395
+ async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
396
+ """Return all resource templates."""
397
+ return [v for v in self._components.values() if isinstance(v, ResourceTemplate)]
398
+
399
+ async def _get_resource_template(
400
+ self, uri: str, version: VersionSpec | None = None
401
+ ) -> ResourceTemplate | None:
402
+ """Get a resource template that matches the given URI.
403
+
404
+ Args:
405
+ uri: The URI to match against templates.
406
+ version: Optional version filter. If None, returns highest version.
407
+ """
408
+ # Find all templates that match the URI
409
+ matching = [
410
+ component
411
+ for component in self._components.values()
412
+ if isinstance(component, ResourceTemplate)
413
+ and component.matches(uri) is not None
414
+ ]
415
+ if version:
416
+ matching = [t for t in matching if version.matches(t.version)]
417
+ if not matching:
418
+ return None
419
+ return max(matching, key=version_sort_key) # type: ignore[type-var]
420
+
421
+ async def _list_prompts(self) -> Sequence[Prompt]:
422
+ """Return all prompts."""
423
+ return [v for v in self._components.values() if isinstance(v, Prompt)]
424
+
425
+ async def _get_prompt(
426
+ self, name: str, version: VersionSpec | None = None
427
+ ) -> Prompt | None:
428
+ """Get a prompt by name.
429
+
430
+ Args:
431
+ name: The prompt name.
432
+ version: Optional version filter. If None, returns highest version.
433
+ """
434
+ matching = [
435
+ v
436
+ for v in self._components.values()
437
+ if isinstance(v, Prompt) and v.name == name
438
+ ]
439
+ if version:
440
+ matching = [p for p in matching if version.matches(p.version)]
441
+ if not matching:
442
+ return None
443
+ return max(matching, key=version_sort_key) # type: ignore[type-var]
444
+
445
+ # =========================================================================
446
+ # Task registration
447
+ # =========================================================================
448
+
449
+ async def get_tasks(self) -> Sequence[FastMCPComponent]:
450
+ """Return components eligible for background task execution.
451
+
452
+ Returns components that have task_config.mode != 'forbidden'.
453
+ This includes both FunctionTool/Resource/Prompt instances created via
454
+ decorators and custom Tool/Resource/Prompt subclasses.
455
+ """
456
+ return [c for c in self._components.values() if c.task_config.supports_tasks()]
457
+
458
+ # =========================================================================
459
+ # Decorator methods
460
+ # =========================================================================
461
+ # Note: Decorator methods (tool, resource, prompt, add_tool, add_resource,
462
+ # add_template, add_prompt) are provided by mixin classes:
463
+ # - ToolDecoratorMixin
464
+ # - ResourceDecoratorMixin
465
+ # - PromptDecoratorMixin
@@ -0,0 +1,39 @@
1
+ """OpenAPI provider for FastMCP.
2
+
3
+ This module provides OpenAPI integration for FastMCP through the Provider pattern.
4
+
5
+ Example:
6
+ ```python
7
+ from fastmcp import FastMCP
8
+ from fastmcp.server.providers.openapi import OpenAPIProvider
9
+ import httpx
10
+
11
+ client = httpx.AsyncClient(base_url="https://api.example.com")
12
+ provider = OpenAPIProvider(openapi_spec=spec, client=client)
13
+ mcp = FastMCP("API Server", providers=[provider])
14
+ ```
15
+ """
16
+
17
+ from fastmcp.server.providers.openapi.components import (
18
+ OpenAPIResource,
19
+ OpenAPIResourceTemplate,
20
+ OpenAPITool,
21
+ )
22
+ from fastmcp.server.providers.openapi.provider import OpenAPIProvider
23
+ from fastmcp.server.providers.openapi.routing import (
24
+ ComponentFn,
25
+ MCPType,
26
+ RouteMap,
27
+ RouteMapFn,
28
+ )
29
+
30
+ __all__ = [
31
+ "ComponentFn",
32
+ "MCPType",
33
+ "OpenAPIProvider",
34
+ "OpenAPIResource",
35
+ "OpenAPIResourceTemplate",
36
+ "OpenAPITool",
37
+ "RouteMap",
38
+ "RouteMapFn",
39
+ ]