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,332 @@
1
+ """OpenAPI component classes: Tool, Resource, and ResourceTemplate."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ import warnings
8
+ from collections.abc import Callable
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ import httpx
12
+ from mcp.types import ToolAnnotations
13
+ from pydantic.networks import AnyUrl
14
+
15
+ import fastmcp
16
+ from fastmcp.resources import (
17
+ Resource,
18
+ ResourceContent,
19
+ ResourceResult,
20
+ ResourceTemplate,
21
+ )
22
+ from fastmcp.server.dependencies import get_http_headers
23
+ from fastmcp.server.tasks.config import TaskConfig
24
+ from fastmcp.tools.tool import Tool, ToolResult
25
+ from fastmcp.utilities.logging import get_logger
26
+ from fastmcp.utilities.openapi import HTTPRoute
27
+ from fastmcp.utilities.openapi.director import RequestDirector
28
+
29
+ if TYPE_CHECKING:
30
+ from fastmcp.server import Context
31
+
32
+ __all__ = [
33
+ "OpenAPIResource",
34
+ "OpenAPIResourceTemplate",
35
+ "OpenAPITool",
36
+ ]
37
+
38
+ logger = get_logger(__name__)
39
+
40
+
41
+ def _slugify(text: str) -> str:
42
+ """Convert text to a URL-friendly slug format.
43
+
44
+ Only contains lowercase letters, uppercase letters, numbers, and underscores.
45
+ """
46
+ if not text:
47
+ return ""
48
+
49
+ # Replace spaces and common separators with underscores
50
+ slug = re.sub(r"[\s\-\.]+", "_", text)
51
+
52
+ # Remove non-alphanumeric characters except underscores
53
+ slug = re.sub(r"[^a-zA-Z0-9_]", "", slug)
54
+
55
+ # Remove multiple consecutive underscores
56
+ slug = re.sub(r"_+", "_", slug)
57
+
58
+ # Remove leading/trailing underscores
59
+ slug = slug.strip("_")
60
+
61
+ return slug
62
+
63
+
64
+ class OpenAPITool(Tool):
65
+ """Tool implementation for OpenAPI endpoints."""
66
+
67
+ task_config: TaskConfig = TaskConfig(mode="forbidden")
68
+
69
+ def __init__(
70
+ self,
71
+ client: httpx.AsyncClient,
72
+ route: HTTPRoute,
73
+ director: RequestDirector,
74
+ name: str,
75
+ description: str,
76
+ parameters: dict[str, Any],
77
+ output_schema: dict[str, Any] | None = None,
78
+ tags: set[str] | None = None,
79
+ timeout: float | None = None,
80
+ annotations: ToolAnnotations | None = None,
81
+ serializer: Callable[[Any], str] | None = None, # Deprecated
82
+ ):
83
+ if serializer is not None and fastmcp.settings.deprecation_warnings:
84
+ warnings.warn(
85
+ "The `serializer` parameter is deprecated. "
86
+ "Return ToolResult from your tools for full control over serialization. "
87
+ "See https://gofastmcp.com/servers/tools#custom-serialization for migration examples.",
88
+ DeprecationWarning,
89
+ stacklevel=2,
90
+ )
91
+ super().__init__(
92
+ name=name,
93
+ description=description,
94
+ parameters=parameters,
95
+ output_schema=output_schema,
96
+ tags=tags or set(),
97
+ annotations=annotations,
98
+ serializer=serializer,
99
+ )
100
+ self._client = client
101
+ self._route = route
102
+ self._director = director
103
+ self._timeout = timeout
104
+
105
+ def __repr__(self) -> str:
106
+ return f"OpenAPITool(name={self.name!r}, method={self._route.method}, path={self._route.path})"
107
+
108
+ async def run(self, arguments: dict[str, Any]) -> ToolResult:
109
+ """Execute the HTTP request using RequestDirector."""
110
+ try:
111
+ base_url = str(self._client.base_url) or "http://localhost"
112
+
113
+ # Build the request using RequestDirector
114
+ request = self._director.build(self._route, arguments, base_url)
115
+
116
+ # Add client headers (lowest precedence)
117
+ if self._client.headers:
118
+ for key, value in self._client.headers.items():
119
+ if key not in request.headers:
120
+ request.headers[key] = value
121
+
122
+ # Add MCP transport headers (highest precedence)
123
+ mcp_headers = get_http_headers()
124
+ if mcp_headers:
125
+ request.headers.update(mcp_headers)
126
+
127
+ logger.debug(f"run - sending request; headers: {request.headers}")
128
+
129
+ response = await self._client.send(request)
130
+ response.raise_for_status()
131
+
132
+ # Try to parse as JSON first
133
+ try:
134
+ result = response.json()
135
+
136
+ # Handle structured content based on output schema
137
+ if self.output_schema is not None:
138
+ if self.output_schema.get("x-fastmcp-wrap-result"):
139
+ structured_output = {"result": result}
140
+ else:
141
+ structured_output = result
142
+ elif not isinstance(result, dict):
143
+ structured_output = {"result": result}
144
+ else:
145
+ structured_output = result
146
+
147
+ return ToolResult(structured_content=structured_output)
148
+ except json.JSONDecodeError:
149
+ return ToolResult(content=response.text)
150
+
151
+ except httpx.HTTPStatusError as e:
152
+ error_message = (
153
+ f"HTTP error {e.response.status_code}: {e.response.reason_phrase}"
154
+ )
155
+ try:
156
+ error_data = e.response.json()
157
+ error_message += f" - {error_data}"
158
+ except (json.JSONDecodeError, ValueError):
159
+ if e.response.text:
160
+ error_message += f" - {e.response.text}"
161
+ raise ValueError(error_message) from e
162
+
163
+ except httpx.RequestError as e:
164
+ raise ValueError(f"Request error: {e!s}") from e
165
+
166
+
167
+ class OpenAPIResource(Resource):
168
+ """Resource implementation for OpenAPI endpoints."""
169
+
170
+ task_config: TaskConfig = TaskConfig(mode="forbidden")
171
+
172
+ def __init__(
173
+ self,
174
+ client: httpx.AsyncClient,
175
+ route: HTTPRoute,
176
+ director: RequestDirector,
177
+ uri: str,
178
+ name: str,
179
+ description: str,
180
+ mime_type: str = "application/json",
181
+ tags: set[str] | None = None,
182
+ timeout: float | None = None,
183
+ ):
184
+ super().__init__(
185
+ uri=AnyUrl(uri),
186
+ name=name,
187
+ description=description,
188
+ mime_type=mime_type,
189
+ tags=tags or set(),
190
+ )
191
+ self._client = client
192
+ self._route = route
193
+ self._director = director
194
+ self._timeout = timeout
195
+
196
+ def __repr__(self) -> str:
197
+ return f"OpenAPIResource(name={self.name!r}, uri={self.uri!r}, path={self._route.path})"
198
+
199
+ async def read(self) -> ResourceResult:
200
+ """Fetch the resource data by making an HTTP request."""
201
+ try:
202
+ path = self._route.path
203
+ resource_uri = str(self.uri)
204
+
205
+ # If this is a templated resource, extract path parameters from the URI
206
+ if "{" in path and "}" in path:
207
+ parts = resource_uri.split("/")
208
+
209
+ if len(parts) > 1:
210
+ path_params = {}
211
+ param_matches = re.findall(r"\{([^}]+)\}", path)
212
+ if param_matches:
213
+ param_matches.sort(reverse=True)
214
+ expected_param_count = len(parts) - 1
215
+ for i, param_name in enumerate(param_matches):
216
+ if i < expected_param_count:
217
+ param_value = parts[-1 - i]
218
+ path_params[param_name] = param_value
219
+
220
+ for param_name, param_value in path_params.items():
221
+ path = path.replace(f"{{{param_name}}}", str(param_value))
222
+
223
+ # Build headers with correct precedence
224
+ headers: dict[str, str] = {}
225
+ if self._client.headers:
226
+ headers.update(self._client.headers)
227
+ mcp_headers = get_http_headers()
228
+ if mcp_headers:
229
+ headers.update(mcp_headers)
230
+
231
+ response = await self._client.request(
232
+ method=self._route.method,
233
+ url=path,
234
+ headers=headers,
235
+ timeout=self._timeout,
236
+ )
237
+ response.raise_for_status()
238
+
239
+ content_type = response.headers.get("content-type", "").lower()
240
+
241
+ if "application/json" in content_type:
242
+ result = response.json()
243
+ return ResourceResult(
244
+ contents=[
245
+ ResourceContent(
246
+ content=json.dumps(result), mime_type="application/json"
247
+ )
248
+ ]
249
+ )
250
+ elif any(ct in content_type for ct in ["text/", "application/xml"]):
251
+ return ResourceResult(
252
+ contents=[
253
+ ResourceContent(content=response.text, mime_type=self.mime_type)
254
+ ]
255
+ )
256
+ else:
257
+ return ResourceResult(
258
+ contents=[
259
+ ResourceContent(
260
+ content=response.content, mime_type=self.mime_type
261
+ )
262
+ ]
263
+ )
264
+
265
+ except httpx.HTTPStatusError as e:
266
+ error_message = (
267
+ f"HTTP error {e.response.status_code}: {e.response.reason_phrase}"
268
+ )
269
+ try:
270
+ error_data = e.response.json()
271
+ error_message += f" - {error_data}"
272
+ except (json.JSONDecodeError, ValueError):
273
+ if e.response.text:
274
+ error_message += f" - {e.response.text}"
275
+ raise ValueError(error_message) from e
276
+
277
+ except httpx.RequestError as e:
278
+ raise ValueError(f"Request error: {e!s}") from e
279
+
280
+
281
+ class OpenAPIResourceTemplate(ResourceTemplate):
282
+ """Resource template implementation for OpenAPI endpoints."""
283
+
284
+ task_config: TaskConfig = TaskConfig(mode="forbidden")
285
+
286
+ def __init__(
287
+ self,
288
+ client: httpx.AsyncClient,
289
+ route: HTTPRoute,
290
+ director: RequestDirector,
291
+ uri_template: str,
292
+ name: str,
293
+ description: str,
294
+ parameters: dict[str, Any],
295
+ tags: set[str] | None = None,
296
+ timeout: float | None = None,
297
+ ):
298
+ super().__init__(
299
+ uri_template=uri_template,
300
+ name=name,
301
+ description=description,
302
+ parameters=parameters,
303
+ tags=tags or set(),
304
+ )
305
+ self._client = client
306
+ self._route = route
307
+ self._director = director
308
+ self._timeout = timeout
309
+
310
+ def __repr__(self) -> str:
311
+ return f"OpenAPIResourceTemplate(name={self.name!r}, uri_template={self.uri_template!r}, path={self._route.path})"
312
+
313
+ async def create_resource(
314
+ self,
315
+ uri: str,
316
+ params: dict[str, Any],
317
+ context: Context | None = None,
318
+ ) -> Resource:
319
+ """Create a resource with the given parameters."""
320
+ uri_parts = [f"{key}={value}" for key, value in params.items()]
321
+
322
+ return OpenAPIResource(
323
+ client=self._client,
324
+ route=self._route,
325
+ director=self._director,
326
+ uri=uri,
327
+ name=f"{self.name}-{'-'.join(uri_parts)}",
328
+ description=self.description or f"Resource for {self._route.path}",
329
+ mime_type="application/json",
330
+ tags=set(self._route.tags or []),
331
+ timeout=self._timeout,
332
+ )