fastmcp 2.10.6__py3-none-any.whl → 2.11.1__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/cli/cli.py +128 -33
- fastmcp/cli/install/claude_code.py +42 -1
- fastmcp/cli/install/claude_desktop.py +42 -1
- fastmcp/cli/install/cursor.py +42 -1
- fastmcp/cli/install/mcp_json.py +41 -0
- fastmcp/cli/run.py +127 -1
- fastmcp/client/__init__.py +2 -0
- fastmcp/client/auth/oauth.py +68 -99
- fastmcp/client/oauth_callback.py +18 -0
- fastmcp/client/transports.py +69 -15
- fastmcp/contrib/component_manager/example.py +2 -2
- fastmcp/experimental/server/openapi/README.md +266 -0
- fastmcp/experimental/server/openapi/__init__.py +38 -0
- fastmcp/experimental/server/openapi/components.py +348 -0
- fastmcp/experimental/server/openapi/routing.py +132 -0
- fastmcp/experimental/server/openapi/server.py +466 -0
- fastmcp/experimental/utilities/openapi/README.md +239 -0
- fastmcp/experimental/utilities/openapi/__init__.py +68 -0
- fastmcp/experimental/utilities/openapi/director.py +208 -0
- fastmcp/experimental/utilities/openapi/formatters.py +355 -0
- fastmcp/experimental/utilities/openapi/json_schema_converter.py +340 -0
- fastmcp/experimental/utilities/openapi/models.py +85 -0
- fastmcp/experimental/utilities/openapi/parser.py +618 -0
- fastmcp/experimental/utilities/openapi/schemas.py +538 -0
- fastmcp/mcp_config.py +125 -88
- fastmcp/prompts/prompt.py +11 -1
- fastmcp/resources/resource.py +21 -1
- fastmcp/resources/template.py +20 -1
- fastmcp/server/auth/__init__.py +18 -2
- fastmcp/server/auth/auth.py +225 -7
- fastmcp/server/auth/providers/bearer.py +25 -473
- fastmcp/server/auth/providers/in_memory.py +4 -2
- fastmcp/server/auth/providers/jwt.py +538 -0
- fastmcp/server/auth/providers/workos.py +151 -0
- fastmcp/server/auth/registry.py +52 -0
- fastmcp/server/context.py +107 -26
- fastmcp/server/dependencies.py +9 -2
- fastmcp/server/http.py +48 -57
- fastmcp/server/middleware/middleware.py +3 -23
- fastmcp/server/openapi.py +1 -1
- fastmcp/server/proxy.py +50 -11
- fastmcp/server/server.py +168 -59
- fastmcp/settings.py +73 -6
- fastmcp/tools/tool.py +36 -3
- fastmcp/tools/tool_manager.py +38 -2
- fastmcp/tools/tool_transform.py +112 -3
- fastmcp/utilities/components.py +41 -3
- fastmcp/utilities/json_schema.py +136 -98
- fastmcp/utilities/json_schema_type.py +1 -3
- fastmcp/utilities/mcp_config.py +28 -0
- fastmcp/utilities/openapi.py +243 -57
- fastmcp/utilities/tests.py +54 -6
- fastmcp/utilities/types.py +94 -11
- {fastmcp-2.10.6.dist-info → fastmcp-2.11.1.dist-info}/METADATA +4 -3
- fastmcp-2.11.1.dist-info/RECORD +108 -0
- fastmcp/server/auth/providers/bearer_env.py +0 -63
- fastmcp/utilities/cache.py +0 -26
- fastmcp-2.10.6.dist-info/RECORD +0 -93
- {fastmcp-2.10.6.dist-info → fastmcp-2.11.1.dist-info}/WHEEL +0 -0
- {fastmcp-2.10.6.dist-info → fastmcp-2.11.1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.10.6.dist-info → fastmcp-2.11.1.dist-info}/licenses/LICENSE +0 -0
fastmcp/server/proxy.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Any, cast
|
|
|
7
7
|
from urllib.parse import quote
|
|
8
8
|
|
|
9
9
|
import mcp.types
|
|
10
|
+
from mcp import ServerSession
|
|
10
11
|
from mcp.client.session import ClientSession
|
|
11
12
|
from mcp.shared.context import LifespanContextT, RequestContext
|
|
12
13
|
from mcp.shared.exceptions import McpError
|
|
@@ -36,6 +37,9 @@ from fastmcp.server.dependencies import get_context
|
|
|
36
37
|
from fastmcp.server.server import FastMCP
|
|
37
38
|
from fastmcp.tools.tool import Tool, ToolResult
|
|
38
39
|
from fastmcp.tools.tool_manager import ToolManager
|
|
40
|
+
from fastmcp.tools.tool_transform import (
|
|
41
|
+
apply_transformations_to_tools,
|
|
42
|
+
)
|
|
39
43
|
from fastmcp.utilities.components import MirroredComponent
|
|
40
44
|
from fastmcp.utilities.logging import get_logger
|
|
41
45
|
|
|
@@ -71,7 +75,12 @@ class ProxyToolManager(ToolManager):
|
|
|
71
75
|
else:
|
|
72
76
|
raise e
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
transformed_tools = apply_transformations_to_tools(
|
|
79
|
+
tools=all_tools,
|
|
80
|
+
transformations=self.transformations,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return transformed_tools
|
|
75
84
|
|
|
76
85
|
async def list_tools(self) -> list[Tool]:
|
|
77
86
|
"""Gets the filtered list of tools including local, mounted, and proxy tools."""
|
|
@@ -246,6 +255,8 @@ class ProxyTool(Tool, MirroredComponent):
|
|
|
246
255
|
parameters=mcp_tool.inputSchema,
|
|
247
256
|
annotations=mcp_tool.annotations,
|
|
248
257
|
output_schema=mcp_tool.outputSchema,
|
|
258
|
+
meta=mcp_tool.meta,
|
|
259
|
+
tags=(mcp_tool.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
249
260
|
_mirrored=True,
|
|
250
261
|
)
|
|
251
262
|
|
|
@@ -294,12 +305,15 @@ class ProxyResource(Resource, MirroredComponent):
|
|
|
294
305
|
mcp_resource: mcp.types.Resource,
|
|
295
306
|
) -> ProxyResource:
|
|
296
307
|
"""Factory method to create a ProxyResource from a raw MCP resource schema."""
|
|
308
|
+
|
|
297
309
|
return cls(
|
|
298
310
|
client=client,
|
|
299
311
|
uri=mcp_resource.uri,
|
|
300
312
|
name=mcp_resource.name,
|
|
301
313
|
description=mcp_resource.description,
|
|
302
314
|
mime_type=mcp_resource.mimeType or "text/plain",
|
|
315
|
+
meta=mcp_resource.meta,
|
|
316
|
+
tags=(mcp_resource.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
303
317
|
_mirrored=True,
|
|
304
318
|
)
|
|
305
319
|
|
|
@@ -339,6 +353,8 @@ class ProxyTemplate(ResourceTemplate, MirroredComponent):
|
|
|
339
353
|
description=mcp_template.description,
|
|
340
354
|
mime_type=mcp_template.mimeType or "text/plain",
|
|
341
355
|
parameters={}, # Remote templates don't have local parameters
|
|
356
|
+
meta=mcp_template.meta,
|
|
357
|
+
tags=(mcp_template.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
342
358
|
_mirrored=True,
|
|
343
359
|
)
|
|
344
360
|
|
|
@@ -371,6 +387,8 @@ class ProxyTemplate(ResourceTemplate, MirroredComponent):
|
|
|
371
387
|
name=self.name,
|
|
372
388
|
description=self.description,
|
|
373
389
|
mime_type=result[0].mimeType,
|
|
390
|
+
meta=self.meta,
|
|
391
|
+
tags=(self.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
374
392
|
_value=value,
|
|
375
393
|
)
|
|
376
394
|
|
|
@@ -404,6 +422,8 @@ class ProxyPrompt(Prompt, MirroredComponent):
|
|
|
404
422
|
name=mcp_prompt.name,
|
|
405
423
|
description=mcp_prompt.description,
|
|
406
424
|
arguments=arguments,
|
|
425
|
+
meta=mcp_prompt.meta,
|
|
426
|
+
tags=(mcp_prompt.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
407
427
|
_mirrored=True,
|
|
408
428
|
)
|
|
409
429
|
|
|
@@ -469,7 +489,11 @@ class FastMCPProxy(FastMCP):
|
|
|
469
489
|
raise ValueError("Must specify 'client_factory'")
|
|
470
490
|
|
|
471
491
|
# Replace the default managers with our specialized proxy managers.
|
|
472
|
-
self._tool_manager = ProxyToolManager(
|
|
492
|
+
self._tool_manager = ProxyToolManager(
|
|
493
|
+
client_factory=self.client_factory,
|
|
494
|
+
# Propagate the transformations from the base class tool manager
|
|
495
|
+
transformations=self._tool_manager.transformations,
|
|
496
|
+
)
|
|
473
497
|
self._resource_manager = ProxyResourceManager(
|
|
474
498
|
client_factory=self.client_factory
|
|
475
499
|
)
|
|
@@ -554,11 +578,12 @@ class ProxyClient(Client[ClientTransportT]):
|
|
|
554
578
|
A handler that forwards the elicitation request from the remote server to the proxy's connected clients and relays the response back to the remote server.
|
|
555
579
|
"""
|
|
556
580
|
ctx = get_context()
|
|
557
|
-
result = await ctx.elicit(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
581
|
+
result = await ctx.session.elicit(
|
|
582
|
+
message=message,
|
|
583
|
+
requestedSchema=params.requestedSchema,
|
|
584
|
+
related_request_id=ctx.request_id,
|
|
585
|
+
)
|
|
586
|
+
return ElicitResult(action=result.action, content=result.content)
|
|
562
587
|
|
|
563
588
|
@classmethod
|
|
564
589
|
async def default_log_handler(cls, message: LogMessage) -> None:
|
|
@@ -566,7 +591,9 @@ class ProxyClient(Client[ClientTransportT]):
|
|
|
566
591
|
A handler that forwards the log notification from the remote server to the proxy's connected clients.
|
|
567
592
|
"""
|
|
568
593
|
ctx = get_context()
|
|
569
|
-
|
|
594
|
+
msg = message.data.get("msg")
|
|
595
|
+
extra = message.data.get("extra")
|
|
596
|
+
await ctx.log(msg, level=message.level, logger_name=message.logger, extra=extra)
|
|
570
597
|
|
|
571
598
|
@classmethod
|
|
572
599
|
async def default_progress_handler(
|
|
@@ -593,6 +620,10 @@ class StatefulProxyClient(ProxyClient[ClientTransportT]):
|
|
|
593
620
|
Note that it is essential to ensure that the proxy server itself is also stateful.
|
|
594
621
|
"""
|
|
595
622
|
|
|
623
|
+
def __init__(self, *args, **kwargs):
|
|
624
|
+
super().__init__(*args, **kwargs)
|
|
625
|
+
self._caches: dict[ServerSession, Client[ClientTransportT]] = {}
|
|
626
|
+
|
|
596
627
|
async def __aexit__(self, exc_type, exc_value, traceback) -> None:
|
|
597
628
|
"""
|
|
598
629
|
The stateful proxy client will be forced disconnected when the session is exited.
|
|
@@ -600,6 +631,14 @@ class StatefulProxyClient(ProxyClient[ClientTransportT]):
|
|
|
600
631
|
"""
|
|
601
632
|
pass
|
|
602
633
|
|
|
634
|
+
async def clear(self):
|
|
635
|
+
"""
|
|
636
|
+
Clear all cached clients and force disconnect them.
|
|
637
|
+
"""
|
|
638
|
+
while self._caches:
|
|
639
|
+
_, cache = self._caches.popitem()
|
|
640
|
+
await cache._disconnect(force=True)
|
|
641
|
+
|
|
603
642
|
def new_stateful(self) -> Client[ClientTransportT]:
|
|
604
643
|
"""
|
|
605
644
|
Create a new stateful proxy client instance with the same configuration.
|
|
@@ -607,15 +646,15 @@ class StatefulProxyClient(ProxyClient[ClientTransportT]):
|
|
|
607
646
|
Use this method as the client factory for stateful proxy server.
|
|
608
647
|
"""
|
|
609
648
|
session = get_context().session
|
|
610
|
-
proxy_client =
|
|
649
|
+
proxy_client = self._caches.get(session, None)
|
|
611
650
|
|
|
612
651
|
if proxy_client is None:
|
|
613
652
|
proxy_client = self.new()
|
|
614
653
|
logger.debug(f"{proxy_client} created for {session}")
|
|
615
|
-
|
|
654
|
+
self._caches[session] = proxy_client
|
|
616
655
|
|
|
617
656
|
async def _on_session_exit():
|
|
618
|
-
|
|
657
|
+
self._caches.pop(session)
|
|
619
658
|
logger.debug(f"{proxy_client} will be disconnect")
|
|
620
659
|
await proxy_client._disconnect(force=True)
|
|
621
660
|
|