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.
- fastmcp/_vendor/__init__.py +1 -0
- fastmcp/_vendor/docket_di/README.md +7 -0
- fastmcp/_vendor/docket_di/__init__.py +163 -0
- fastmcp/cli/cli.py +112 -28
- fastmcp/cli/install/claude_code.py +1 -5
- fastmcp/cli/install/claude_desktop.py +1 -5
- fastmcp/cli/install/cursor.py +1 -5
- fastmcp/cli/install/gemini_cli.py +1 -5
- fastmcp/cli/install/mcp_json.py +1 -6
- fastmcp/cli/run.py +146 -5
- fastmcp/client/__init__.py +7 -9
- fastmcp/client/auth/oauth.py +18 -17
- fastmcp/client/client.py +100 -870
- fastmcp/client/elicitation.py +1 -1
- fastmcp/client/mixins/__init__.py +13 -0
- fastmcp/client/mixins/prompts.py +295 -0
- fastmcp/client/mixins/resources.py +325 -0
- fastmcp/client/mixins/task_management.py +157 -0
- fastmcp/client/mixins/tools.py +397 -0
- fastmcp/client/sampling/handlers/anthropic.py +2 -2
- fastmcp/client/sampling/handlers/openai.py +1 -1
- fastmcp/client/tasks.py +3 -3
- fastmcp/client/telemetry.py +47 -0
- fastmcp/client/transports/__init__.py +38 -0
- fastmcp/client/transports/base.py +82 -0
- fastmcp/client/transports/config.py +170 -0
- fastmcp/client/transports/http.py +145 -0
- fastmcp/client/transports/inference.py +154 -0
- fastmcp/client/transports/memory.py +90 -0
- fastmcp/client/transports/sse.py +89 -0
- fastmcp/client/transports/stdio.py +543 -0
- fastmcp/contrib/component_manager/README.md +4 -10
- fastmcp/contrib/component_manager/__init__.py +1 -2
- fastmcp/contrib/component_manager/component_manager.py +95 -160
- fastmcp/contrib/component_manager/example.py +1 -1
- fastmcp/contrib/mcp_mixin/example.py +4 -4
- fastmcp/contrib/mcp_mixin/mcp_mixin.py +11 -4
- fastmcp/decorators.py +41 -0
- fastmcp/dependencies.py +12 -1
- fastmcp/exceptions.py +4 -0
- fastmcp/experimental/server/openapi/__init__.py +18 -15
- fastmcp/mcp_config.py +13 -4
- fastmcp/prompts/__init__.py +6 -3
- fastmcp/prompts/function_prompt.py +465 -0
- fastmcp/prompts/prompt.py +321 -271
- fastmcp/resources/__init__.py +5 -3
- fastmcp/resources/function_resource.py +335 -0
- fastmcp/resources/resource.py +325 -115
- fastmcp/resources/template.py +215 -43
- fastmcp/resources/types.py +27 -12
- fastmcp/server/__init__.py +2 -2
- fastmcp/server/auth/__init__.py +14 -0
- fastmcp/server/auth/auth.py +30 -10
- fastmcp/server/auth/authorization.py +190 -0
- fastmcp/server/auth/oauth_proxy/__init__.py +14 -0
- fastmcp/server/auth/oauth_proxy/consent.py +361 -0
- fastmcp/server/auth/oauth_proxy/models.py +178 -0
- fastmcp/server/auth/{oauth_proxy.py → oauth_proxy/proxy.py} +24 -778
- fastmcp/server/auth/oauth_proxy/ui.py +277 -0
- fastmcp/server/auth/oidc_proxy.py +2 -2
- fastmcp/server/auth/providers/auth0.py +24 -94
- fastmcp/server/auth/providers/aws.py +26 -95
- fastmcp/server/auth/providers/azure.py +41 -129
- fastmcp/server/auth/providers/descope.py +18 -49
- fastmcp/server/auth/providers/discord.py +25 -86
- fastmcp/server/auth/providers/github.py +23 -87
- fastmcp/server/auth/providers/google.py +24 -87
- fastmcp/server/auth/providers/introspection.py +60 -79
- fastmcp/server/auth/providers/jwt.py +30 -67
- fastmcp/server/auth/providers/oci.py +47 -110
- fastmcp/server/auth/providers/scalekit.py +23 -61
- fastmcp/server/auth/providers/supabase.py +18 -47
- fastmcp/server/auth/providers/workos.py +34 -127
- fastmcp/server/context.py +372 -419
- fastmcp/server/dependencies.py +541 -251
- fastmcp/server/elicitation.py +20 -18
- fastmcp/server/event_store.py +3 -3
- fastmcp/server/http.py +16 -6
- fastmcp/server/lifespan.py +198 -0
- fastmcp/server/low_level.py +92 -2
- fastmcp/server/middleware/__init__.py +5 -1
- fastmcp/server/middleware/authorization.py +312 -0
- fastmcp/server/middleware/caching.py +101 -54
- fastmcp/server/middleware/middleware.py +6 -9
- fastmcp/server/middleware/ping.py +70 -0
- fastmcp/server/middleware/tool_injection.py +2 -2
- fastmcp/server/mixins/__init__.py +7 -0
- fastmcp/server/mixins/lifespan.py +217 -0
- fastmcp/server/mixins/mcp_operations.py +392 -0
- fastmcp/server/mixins/transport.py +342 -0
- fastmcp/server/openapi/__init__.py +41 -21
- fastmcp/server/openapi/components.py +16 -339
- fastmcp/server/openapi/routing.py +34 -118
- fastmcp/server/openapi/server.py +67 -392
- fastmcp/server/providers/__init__.py +71 -0
- fastmcp/server/providers/aggregate.py +261 -0
- fastmcp/server/providers/base.py +578 -0
- fastmcp/server/providers/fastmcp_provider.py +674 -0
- fastmcp/server/providers/filesystem.py +226 -0
- fastmcp/server/providers/filesystem_discovery.py +327 -0
- fastmcp/server/providers/local_provider/__init__.py +11 -0
- fastmcp/server/providers/local_provider/decorators/__init__.py +15 -0
- fastmcp/server/providers/local_provider/decorators/prompts.py +256 -0
- fastmcp/server/providers/local_provider/decorators/resources.py +240 -0
- fastmcp/server/providers/local_provider/decorators/tools.py +315 -0
- fastmcp/server/providers/local_provider/local_provider.py +465 -0
- fastmcp/server/providers/openapi/__init__.py +39 -0
- fastmcp/server/providers/openapi/components.py +332 -0
- fastmcp/server/providers/openapi/provider.py +405 -0
- fastmcp/server/providers/openapi/routing.py +109 -0
- fastmcp/server/providers/proxy.py +867 -0
- fastmcp/server/providers/skills/__init__.py +59 -0
- fastmcp/server/providers/skills/_common.py +101 -0
- fastmcp/server/providers/skills/claude_provider.py +44 -0
- fastmcp/server/providers/skills/directory_provider.py +153 -0
- fastmcp/server/providers/skills/skill_provider.py +432 -0
- fastmcp/server/providers/skills/vendor_providers.py +142 -0
- fastmcp/server/providers/wrapped_provider.py +140 -0
- fastmcp/server/proxy.py +34 -700
- fastmcp/server/sampling/run.py +341 -2
- fastmcp/server/sampling/sampling_tool.py +4 -3
- fastmcp/server/server.py +1214 -2171
- fastmcp/server/tasks/__init__.py +2 -1
- fastmcp/server/tasks/capabilities.py +13 -1
- fastmcp/server/tasks/config.py +66 -3
- fastmcp/server/tasks/handlers.py +65 -273
- fastmcp/server/tasks/keys.py +4 -6
- fastmcp/server/tasks/requests.py +474 -0
- fastmcp/server/tasks/routing.py +76 -0
- fastmcp/server/tasks/subscriptions.py +20 -11
- fastmcp/server/telemetry.py +131 -0
- fastmcp/server/transforms/__init__.py +244 -0
- fastmcp/server/transforms/namespace.py +193 -0
- fastmcp/server/transforms/prompts_as_tools.py +175 -0
- fastmcp/server/transforms/resources_as_tools.py +190 -0
- fastmcp/server/transforms/tool_transform.py +96 -0
- fastmcp/server/transforms/version_filter.py +124 -0
- fastmcp/server/transforms/visibility.py +526 -0
- fastmcp/settings.py +34 -96
- fastmcp/telemetry.py +122 -0
- fastmcp/tools/__init__.py +10 -3
- fastmcp/tools/function_parsing.py +201 -0
- fastmcp/tools/function_tool.py +467 -0
- fastmcp/tools/tool.py +215 -362
- fastmcp/tools/tool_transform.py +38 -21
- fastmcp/utilities/async_utils.py +69 -0
- fastmcp/utilities/components.py +152 -91
- fastmcp/utilities/inspect.py +8 -20
- fastmcp/utilities/json_schema.py +12 -5
- fastmcp/utilities/json_schema_type.py +17 -15
- fastmcp/utilities/lifespan.py +56 -0
- fastmcp/utilities/logging.py +12 -4
- fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -3
- fastmcp/utilities/openapi/parser.py +3 -3
- fastmcp/utilities/pagination.py +80 -0
- fastmcp/utilities/skills.py +253 -0
- fastmcp/utilities/tests.py +0 -16
- fastmcp/utilities/timeout.py +47 -0
- fastmcp/utilities/types.py +1 -1
- fastmcp/utilities/versions.py +285 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/METADATA +8 -5
- fastmcp-3.0.0b1.dist-info/RECORD +228 -0
- fastmcp/client/transports.py +0 -1170
- fastmcp/contrib/component_manager/component_service.py +0 -209
- fastmcp/prompts/prompt_manager.py +0 -117
- fastmcp/resources/resource_manager.py +0 -338
- fastmcp/server/tasks/converters.py +0 -206
- fastmcp/server/tasks/protocol.py +0 -359
- fastmcp/tools/tool_manager.py +0 -170
- fastmcp/utilities/mcp_config.py +0 -56
- fastmcp-2.14.4.dist-info/RECORD +0 -161
- /fastmcp/server/{openapi → providers/openapi}/README.md +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/WHEEL +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""AggregateProvider for combining multiple providers into one.
|
|
2
|
+
|
|
3
|
+
This module provides `AggregateProvider`, a utility class that presents
|
|
4
|
+
multiple providers as a single unified provider. Useful when you want to
|
|
5
|
+
combine custom providers without creating a full FastMCP server.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
```python
|
|
9
|
+
from fastmcp.server.providers import AggregateProvider
|
|
10
|
+
|
|
11
|
+
# Combine multiple providers into one
|
|
12
|
+
combined = AggregateProvider()
|
|
13
|
+
combined.add_provider(provider1)
|
|
14
|
+
combined.add_provider(provider2, namespace="api") # Tools become "api_foo"
|
|
15
|
+
|
|
16
|
+
# Use like any other provider
|
|
17
|
+
tools = await combined.list_tools()
|
|
18
|
+
```
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
from collections.abc import AsyncIterator, Sequence
|
|
25
|
+
from contextlib import AsyncExitStack, asynccontextmanager
|
|
26
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
27
|
+
|
|
28
|
+
from fastmcp.exceptions import NotFoundError
|
|
29
|
+
from fastmcp.server.providers.base import Provider
|
|
30
|
+
from fastmcp.server.transforms import Namespace
|
|
31
|
+
from fastmcp.utilities.async_utils import gather
|
|
32
|
+
from fastmcp.utilities.components import FastMCPComponent
|
|
33
|
+
from fastmcp.utilities.versions import VersionSpec, version_sort_key
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from fastmcp.prompts.prompt import Prompt
|
|
37
|
+
from fastmcp.resources.resource import Resource
|
|
38
|
+
from fastmcp.resources.template import ResourceTemplate
|
|
39
|
+
from fastmcp.tools.tool import Tool
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
T = TypeVar("T")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AggregateProvider(Provider):
|
|
47
|
+
"""Utility provider that combines multiple providers into one.
|
|
48
|
+
|
|
49
|
+
Components are aggregated from all providers. For get_* operations,
|
|
50
|
+
providers are queried in parallel and the highest version is returned.
|
|
51
|
+
|
|
52
|
+
When adding providers with a namespace, wrap_transform() is used to apply
|
|
53
|
+
the Namespace transform. This means namespace transformation is handled
|
|
54
|
+
by the wrapped provider, not by AggregateProvider.
|
|
55
|
+
|
|
56
|
+
Errors from individual providers are logged and skipped (graceful degradation).
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
```python
|
|
60
|
+
combined = AggregateProvider()
|
|
61
|
+
combined.add_provider(db_provider)
|
|
62
|
+
combined.add_provider(api_provider, namespace="api")
|
|
63
|
+
# db_provider's tools keep original names
|
|
64
|
+
# api_provider's tools become "api_foo", "api_bar", etc.
|
|
65
|
+
```
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self, providers: Sequence[Provider] | None = None) -> None:
|
|
69
|
+
"""Initialize with an optional sequence of providers.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
providers: Optional initial providers (without namespacing).
|
|
73
|
+
For namespaced providers, use add_provider() instead.
|
|
74
|
+
"""
|
|
75
|
+
super().__init__()
|
|
76
|
+
self.providers: list[Provider] = list(providers or [])
|
|
77
|
+
|
|
78
|
+
def add_provider(self, provider: Provider, *, namespace: str = "") -> None:
|
|
79
|
+
"""Add a provider with optional namespace.
|
|
80
|
+
|
|
81
|
+
If the provider is a FastMCP server, it's automatically wrapped in
|
|
82
|
+
FastMCPProvider to ensure middleware is invoked correctly.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
provider: The provider to add.
|
|
86
|
+
namespace: Optional namespace prefix. When set:
|
|
87
|
+
- Tools become "namespace_toolname"
|
|
88
|
+
- Resources become "protocol://namespace/path"
|
|
89
|
+
- Prompts become "namespace_promptname"
|
|
90
|
+
"""
|
|
91
|
+
# Import here to avoid circular imports
|
|
92
|
+
from fastmcp.server.server import FastMCP
|
|
93
|
+
|
|
94
|
+
# Auto-wrap FastMCP servers to ensure middleware is invoked
|
|
95
|
+
if isinstance(provider, FastMCP):
|
|
96
|
+
from fastmcp.server.providers.fastmcp_provider import FastMCPProvider
|
|
97
|
+
|
|
98
|
+
provider = FastMCPProvider(provider)
|
|
99
|
+
|
|
100
|
+
# Apply namespace via wrap_transform if specified
|
|
101
|
+
if namespace:
|
|
102
|
+
provider = provider.wrap_transform(Namespace(namespace))
|
|
103
|
+
|
|
104
|
+
self.providers.append(provider)
|
|
105
|
+
|
|
106
|
+
def _collect_list_results(
|
|
107
|
+
self, results: list[Sequence[T] | BaseException], operation: str
|
|
108
|
+
) -> list[T]:
|
|
109
|
+
"""Collect successful list results, logging any exceptions."""
|
|
110
|
+
collected: list[T] = []
|
|
111
|
+
for i, result in enumerate(results):
|
|
112
|
+
if isinstance(result, BaseException):
|
|
113
|
+
logger.debug(
|
|
114
|
+
f"Error during {operation} from provider "
|
|
115
|
+
f"{self.providers[i]}: {result}"
|
|
116
|
+
)
|
|
117
|
+
continue
|
|
118
|
+
collected.extend(result)
|
|
119
|
+
return collected
|
|
120
|
+
|
|
121
|
+
def _get_highest_version_result(
|
|
122
|
+
self,
|
|
123
|
+
results: list[FastMCPComponent | None | BaseException],
|
|
124
|
+
operation: str,
|
|
125
|
+
) -> FastMCPComponent | None:
|
|
126
|
+
"""Get the highest version from successful non-None results.
|
|
127
|
+
|
|
128
|
+
Used for versioned components where we want the highest version
|
|
129
|
+
across all providers rather than the first match.
|
|
130
|
+
"""
|
|
131
|
+
valid: list[FastMCPComponent] = []
|
|
132
|
+
for i, result in enumerate(results):
|
|
133
|
+
if isinstance(result, BaseException):
|
|
134
|
+
if not isinstance(result, NotFoundError):
|
|
135
|
+
logger.debug(
|
|
136
|
+
f"Error during {operation} from provider "
|
|
137
|
+
f"{self.providers[i]}: {result}"
|
|
138
|
+
)
|
|
139
|
+
continue
|
|
140
|
+
if result is not None:
|
|
141
|
+
valid.append(result)
|
|
142
|
+
if not valid:
|
|
143
|
+
return None
|
|
144
|
+
return max(valid, key=version_sort_key)
|
|
145
|
+
|
|
146
|
+
def __repr__(self) -> str:
|
|
147
|
+
return f"AggregateProvider(providers={self.providers!r})"
|
|
148
|
+
|
|
149
|
+
# -------------------------------------------------------------------------
|
|
150
|
+
# Tools
|
|
151
|
+
# -------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
async def _list_tools(self) -> Sequence[Tool]:
|
|
154
|
+
"""List all tools from all providers."""
|
|
155
|
+
results = await gather(
|
|
156
|
+
*[p.list_tools() for p in self.providers],
|
|
157
|
+
return_exceptions=True,
|
|
158
|
+
)
|
|
159
|
+
return self._collect_list_results(results, "list_tools")
|
|
160
|
+
|
|
161
|
+
async def _get_tool(
|
|
162
|
+
self, name: str, version: VersionSpec | None = None
|
|
163
|
+
) -> Tool | None:
|
|
164
|
+
"""Get tool by name from providers."""
|
|
165
|
+
results = await gather(
|
|
166
|
+
*[p.get_tool(name, version) for p in self.providers],
|
|
167
|
+
return_exceptions=True,
|
|
168
|
+
)
|
|
169
|
+
return self._get_highest_version_result(results, f"get_tool({name!r})") # type: ignore[return-value]
|
|
170
|
+
|
|
171
|
+
# -------------------------------------------------------------------------
|
|
172
|
+
# Resources
|
|
173
|
+
# -------------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
async def _list_resources(self) -> Sequence[Resource]:
|
|
176
|
+
"""List all resources from all providers."""
|
|
177
|
+
results = await gather(
|
|
178
|
+
*[p.list_resources() for p in self.providers],
|
|
179
|
+
return_exceptions=True,
|
|
180
|
+
)
|
|
181
|
+
return self._collect_list_results(results, "list_resources")
|
|
182
|
+
|
|
183
|
+
async def _get_resource(
|
|
184
|
+
self, uri: str, version: VersionSpec | None = None
|
|
185
|
+
) -> Resource | None:
|
|
186
|
+
"""Get resource by URI from providers."""
|
|
187
|
+
results = await gather(
|
|
188
|
+
*[p.get_resource(uri, version) for p in self.providers],
|
|
189
|
+
return_exceptions=True,
|
|
190
|
+
)
|
|
191
|
+
return self._get_highest_version_result(results, f"get_resource({uri!r})") # type: ignore[return-value]
|
|
192
|
+
|
|
193
|
+
# -------------------------------------------------------------------------
|
|
194
|
+
# Resource Templates
|
|
195
|
+
# -------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
|
198
|
+
"""List all resource templates from all providers."""
|
|
199
|
+
results = await gather(
|
|
200
|
+
*[p.list_resource_templates() for p in self.providers],
|
|
201
|
+
return_exceptions=True,
|
|
202
|
+
)
|
|
203
|
+
return self._collect_list_results(results, "list_resource_templates")
|
|
204
|
+
|
|
205
|
+
async def _get_resource_template(
|
|
206
|
+
self, uri: str, version: VersionSpec | None = None
|
|
207
|
+
) -> ResourceTemplate | None:
|
|
208
|
+
"""Get resource template by URI from providers."""
|
|
209
|
+
results = await gather(
|
|
210
|
+
*[p.get_resource_template(uri, version) for p in self.providers],
|
|
211
|
+
return_exceptions=True,
|
|
212
|
+
)
|
|
213
|
+
return self._get_highest_version_result(
|
|
214
|
+
results, f"get_resource_template({uri!r})"
|
|
215
|
+
) # type: ignore[return-value]
|
|
216
|
+
|
|
217
|
+
# -------------------------------------------------------------------------
|
|
218
|
+
# Prompts
|
|
219
|
+
# -------------------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
async def _list_prompts(self) -> Sequence[Prompt]:
|
|
222
|
+
"""List all prompts from all providers."""
|
|
223
|
+
results = await gather(
|
|
224
|
+
*[p.list_prompts() for p in self.providers],
|
|
225
|
+
return_exceptions=True,
|
|
226
|
+
)
|
|
227
|
+
return self._collect_list_results(results, "list_prompts")
|
|
228
|
+
|
|
229
|
+
async def _get_prompt(
|
|
230
|
+
self, name: str, version: VersionSpec | None = None
|
|
231
|
+
) -> Prompt | None:
|
|
232
|
+
"""Get prompt by name from providers."""
|
|
233
|
+
results = await gather(
|
|
234
|
+
*[p.get_prompt(name, version) for p in self.providers],
|
|
235
|
+
return_exceptions=True,
|
|
236
|
+
)
|
|
237
|
+
return self._get_highest_version_result(results, f"get_prompt({name!r})") # type: ignore[return-value]
|
|
238
|
+
|
|
239
|
+
# -------------------------------------------------------------------------
|
|
240
|
+
# Tasks
|
|
241
|
+
# -------------------------------------------------------------------------
|
|
242
|
+
|
|
243
|
+
async def get_tasks(self) -> Sequence[FastMCPComponent]:
|
|
244
|
+
"""Get all task-eligible components from all providers."""
|
|
245
|
+
results = await gather(
|
|
246
|
+
*[p.get_tasks() for p in self.providers],
|
|
247
|
+
return_exceptions=True,
|
|
248
|
+
)
|
|
249
|
+
return self._collect_list_results(results, "get_tasks")
|
|
250
|
+
|
|
251
|
+
# -------------------------------------------------------------------------
|
|
252
|
+
# Lifecycle
|
|
253
|
+
# -------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
@asynccontextmanager
|
|
256
|
+
async def lifespan(self) -> AsyncIterator[None]:
|
|
257
|
+
"""Combine lifespans of all providers."""
|
|
258
|
+
async with AsyncExitStack() as stack:
|
|
259
|
+
for p in self.providers:
|
|
260
|
+
await stack.enter_async_context(p.lifespan())
|
|
261
|
+
yield
|