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/server.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import datetime
|
|
6
5
|
import inspect
|
|
7
6
|
import re
|
|
8
7
|
import warnings
|
|
@@ -25,7 +24,9 @@ from mcp.server.lowlevel.helper_types import ReadResourceContents
|
|
|
25
24
|
from mcp.server.lowlevel.server import LifespanResultT, NotificationOptions
|
|
26
25
|
from mcp.server.stdio import stdio_server
|
|
27
26
|
from mcp.types import (
|
|
27
|
+
Annotations,
|
|
28
28
|
AnyFunction,
|
|
29
|
+
CallToolRequestParams,
|
|
29
30
|
ContentBlock,
|
|
30
31
|
GetPromptResult,
|
|
31
32
|
ToolAnnotations,
|
|
@@ -48,8 +49,8 @@ from fastmcp.prompts import Prompt, PromptManager
|
|
|
48
49
|
from fastmcp.prompts.prompt import FunctionPrompt
|
|
49
50
|
from fastmcp.resources import Resource, ResourceManager
|
|
50
51
|
from fastmcp.resources.template import ResourceTemplate
|
|
51
|
-
from fastmcp.server.auth.auth import
|
|
52
|
-
from fastmcp.server.auth.
|
|
52
|
+
from fastmcp.server.auth.auth import AuthProvider
|
|
53
|
+
from fastmcp.server.auth.registry import get_registered_provider
|
|
53
54
|
from fastmcp.server.http import (
|
|
54
55
|
StarletteWithLifespan,
|
|
55
56
|
create_sse_app,
|
|
@@ -60,7 +61,7 @@ from fastmcp.server.middleware import Middleware, MiddlewareContext
|
|
|
60
61
|
from fastmcp.settings import Settings
|
|
61
62
|
from fastmcp.tools import ToolManager
|
|
62
63
|
from fastmcp.tools.tool import FunctionTool, Tool, ToolResult
|
|
63
|
-
from fastmcp.
|
|
64
|
+
from fastmcp.tools.tool_transform import ToolTransformConfig
|
|
64
65
|
from fastmcp.utilities.cli import log_server_banner
|
|
65
66
|
from fastmcp.utilities.components import FastMCPComponent
|
|
66
67
|
from fastmcp.utilities.logging import get_logger
|
|
@@ -69,10 +70,19 @@ from fastmcp.utilities.types import NotSet, NotSetT
|
|
|
69
70
|
if TYPE_CHECKING:
|
|
70
71
|
from fastmcp.client import Client
|
|
71
72
|
from fastmcp.client.transports import ClientTransport, ClientTransportT
|
|
73
|
+
from fastmcp.experimental.server.openapi import FastMCPOpenAPI as FastMCPOpenAPINew
|
|
74
|
+
from fastmcp.experimental.server.openapi.routing import (
|
|
75
|
+
ComponentFn as OpenAPIComponentFnNew,
|
|
76
|
+
)
|
|
77
|
+
from fastmcp.experimental.server.openapi.routing import RouteMap as RouteMapNew
|
|
78
|
+
from fastmcp.experimental.server.openapi.routing import (
|
|
79
|
+
RouteMapFn as OpenAPIRouteMapFnNew,
|
|
80
|
+
)
|
|
72
81
|
from fastmcp.server.openapi import ComponentFn as OpenAPIComponentFn
|
|
73
82
|
from fastmcp.server.openapi import FastMCPOpenAPI, RouteMap
|
|
74
83
|
from fastmcp.server.openapi import RouteMapFn as OpenAPIRouteMapFn
|
|
75
84
|
from fastmcp.server.proxy import FastMCPProxy
|
|
85
|
+
|
|
76
86
|
logger = get_logger(__name__)
|
|
77
87
|
|
|
78
88
|
DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
|
|
@@ -121,7 +131,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
121
131
|
instructions: str | None = None,
|
|
122
132
|
*,
|
|
123
133
|
version: str | None = None,
|
|
124
|
-
auth:
|
|
134
|
+
auth: AuthProvider | None | NotSetT = NotSet,
|
|
125
135
|
middleware: list[Middleware] | None = None,
|
|
126
136
|
lifespan: (
|
|
127
137
|
Callable[
|
|
@@ -138,9 +148,11 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
138
148
|
resource_prefix_format: Literal["protocol", "path"] | None = None,
|
|
139
149
|
mask_error_details: bool | None = None,
|
|
140
150
|
tools: list[Tool | Callable[..., Any]] | None = None,
|
|
151
|
+
tool_transformations: dict[str, ToolTransformConfig] | None = None,
|
|
141
152
|
dependencies: list[str] | None = None,
|
|
142
153
|
include_tags: set[str] | None = None,
|
|
143
154
|
exclude_tags: set[str] | None = None,
|
|
155
|
+
include_fastmcp_meta: bool | None = None,
|
|
144
156
|
# ---
|
|
145
157
|
# ---
|
|
146
158
|
# --- The following arguments are DEPRECATED ---
|
|
@@ -160,13 +172,11 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
160
172
|
resource_prefix_format or fastmcp.settings.resource_prefix_format
|
|
161
173
|
)
|
|
162
174
|
|
|
163
|
-
self._cache = TimedCache(
|
|
164
|
-
expiration=datetime.timedelta(seconds=cache_expiration_seconds or 0)
|
|
165
|
-
)
|
|
166
175
|
self._additional_http_routes: list[BaseRoute] = []
|
|
167
176
|
self._tool_manager = ToolManager(
|
|
168
177
|
duplicate_behavior=on_duplicate_tools,
|
|
169
178
|
mask_error_details=mask_error_details,
|
|
179
|
+
transformations=tool_transformations,
|
|
170
180
|
)
|
|
171
181
|
self._resource_manager = ResourceManager(
|
|
172
182
|
duplicate_behavior=on_duplicate_resources,
|
|
@@ -190,9 +200,14 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
190
200
|
lifespan=_lifespan_wrapper(self, lifespan),
|
|
191
201
|
)
|
|
192
202
|
|
|
193
|
-
if auth is
|
|
194
|
-
|
|
195
|
-
|
|
203
|
+
# if auth is `NotSet`, try to create a provider from the environment
|
|
204
|
+
if auth is NotSet:
|
|
205
|
+
if fastmcp.settings.server_auth is not None:
|
|
206
|
+
provider_cls = get_registered_provider(fastmcp.settings.server_auth)
|
|
207
|
+
auth = provider_cls()
|
|
208
|
+
else:
|
|
209
|
+
auth = None
|
|
210
|
+
self.auth = cast(AuthProvider | None, auth)
|
|
196
211
|
|
|
197
212
|
if tools:
|
|
198
213
|
for tool in tools:
|
|
@@ -209,6 +224,12 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
209
224
|
self._setup_handlers()
|
|
210
225
|
self.dependencies = dependencies or fastmcp.settings.server_dependencies
|
|
211
226
|
|
|
227
|
+
self.include_fastmcp_meta = (
|
|
228
|
+
include_fastmcp_meta
|
|
229
|
+
if include_fastmcp_meta is not None
|
|
230
|
+
else fastmcp.settings.include_fastmcp_meta
|
|
231
|
+
)
|
|
232
|
+
|
|
212
233
|
# handle deprecated settings
|
|
213
234
|
self._handle_deprecated_settings(
|
|
214
235
|
log_level=log_level,
|
|
@@ -407,7 +428,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
407
428
|
methods: list[str],
|
|
408
429
|
name: str | None = None,
|
|
409
430
|
include_in_schema: bool = True,
|
|
410
|
-
)
|
|
431
|
+
) -> Callable[
|
|
432
|
+
[Callable[[Request], Awaitable[Response]]],
|
|
433
|
+
Callable[[Request], Awaitable[Response]],
|
|
434
|
+
]:
|
|
411
435
|
"""
|
|
412
436
|
Decorator to register a custom HTTP route on the FastMCP server.
|
|
413
437
|
|
|
@@ -453,7 +477,13 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
453
477
|
|
|
454
478
|
async with fastmcp.server.context.Context(fastmcp=self):
|
|
455
479
|
tools = await self._list_tools()
|
|
456
|
-
return [
|
|
480
|
+
return [
|
|
481
|
+
tool.to_mcp_tool(
|
|
482
|
+
name=tool.key,
|
|
483
|
+
include_fastmcp_meta=self.include_fastmcp_meta,
|
|
484
|
+
)
|
|
485
|
+
for tool in tools
|
|
486
|
+
]
|
|
457
487
|
|
|
458
488
|
async def _list_tools(self) -> list[Tool]:
|
|
459
489
|
"""
|
|
@@ -492,7 +522,11 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
492
522
|
async with fastmcp.server.context.Context(fastmcp=self):
|
|
493
523
|
resources = await self._list_resources()
|
|
494
524
|
return [
|
|
495
|
-
resource.to_mcp_resource(
|
|
525
|
+
resource.to_mcp_resource(
|
|
526
|
+
uri=resource.key,
|
|
527
|
+
include_fastmcp_meta=self.include_fastmcp_meta,
|
|
528
|
+
)
|
|
529
|
+
for resource in resources
|
|
496
530
|
]
|
|
497
531
|
|
|
498
532
|
async def _list_resources(self) -> list[Resource]:
|
|
@@ -533,7 +567,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
533
567
|
async with fastmcp.server.context.Context(fastmcp=self):
|
|
534
568
|
templates = await self._list_resource_templates()
|
|
535
569
|
return [
|
|
536
|
-
template.to_mcp_template(
|
|
570
|
+
template.to_mcp_template(
|
|
571
|
+
uriTemplate=template.key,
|
|
572
|
+
include_fastmcp_meta=self.include_fastmcp_meta,
|
|
573
|
+
)
|
|
537
574
|
for template in templates
|
|
538
575
|
]
|
|
539
576
|
|
|
@@ -574,7 +611,13 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
574
611
|
|
|
575
612
|
async with fastmcp.server.context.Context(fastmcp=self):
|
|
576
613
|
prompts = await self._list_prompts()
|
|
577
|
-
return [
|
|
614
|
+
return [
|
|
615
|
+
prompt.to_mcp_prompt(
|
|
616
|
+
name=prompt.key,
|
|
617
|
+
include_fastmcp_meta=self.include_fastmcp_meta,
|
|
618
|
+
)
|
|
619
|
+
for prompt in prompts
|
|
620
|
+
]
|
|
578
621
|
|
|
579
622
|
async def _list_prompts(self) -> list[Prompt]:
|
|
580
623
|
"""
|
|
@@ -650,7 +693,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
650
693
|
key=context.message.name, arguments=context.message.arguments or {}
|
|
651
694
|
)
|
|
652
695
|
|
|
653
|
-
mw_context = MiddlewareContext(
|
|
696
|
+
mw_context = MiddlewareContext[CallToolRequestParams](
|
|
654
697
|
message=mcp.types.CallToolRequestParams(name=key, arguments=arguments),
|
|
655
698
|
source="client",
|
|
656
699
|
type="request",
|
|
@@ -772,7 +815,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
772
815
|
The tool instance that was added to the server.
|
|
773
816
|
"""
|
|
774
817
|
self._tool_manager.add_tool(tool)
|
|
775
|
-
self._cache.clear()
|
|
776
818
|
|
|
777
819
|
# Send notification if we're in a request context
|
|
778
820
|
try:
|
|
@@ -795,7 +837,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
795
837
|
NotFoundError: If the tool is not found
|
|
796
838
|
"""
|
|
797
839
|
self._tool_manager.remove_tool(name)
|
|
798
|
-
self._cache.clear()
|
|
799
840
|
|
|
800
841
|
# Send notification if we're in a request context
|
|
801
842
|
try:
|
|
@@ -806,6 +847,16 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
806
847
|
except RuntimeError:
|
|
807
848
|
pass # No context available
|
|
808
849
|
|
|
850
|
+
def add_tool_transformation(
|
|
851
|
+
self, tool_name: str, transformation: ToolTransformConfig
|
|
852
|
+
) -> None:
|
|
853
|
+
"""Add a tool transformation."""
|
|
854
|
+
self._tool_manager.add_tool_transformation(tool_name, transformation)
|
|
855
|
+
|
|
856
|
+
def remove_tool_transformation(self, tool_name: str) -> None:
|
|
857
|
+
"""Remove a tool transformation."""
|
|
858
|
+
self._tool_manager.remove_tool_transformation(tool_name)
|
|
859
|
+
|
|
809
860
|
@overload
|
|
810
861
|
def tool(
|
|
811
862
|
self,
|
|
@@ -818,6 +869,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
818
869
|
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
819
870
|
annotations: ToolAnnotations | dict[str, Any] | None = None,
|
|
820
871
|
exclude_args: list[str] | None = None,
|
|
872
|
+
meta: dict[str, Any] | None = None,
|
|
821
873
|
enabled: bool | None = None,
|
|
822
874
|
) -> FunctionTool: ...
|
|
823
875
|
|
|
@@ -833,6 +885,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
833
885
|
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
834
886
|
annotations: ToolAnnotations | dict[str, Any] | None = None,
|
|
835
887
|
exclude_args: list[str] | None = None,
|
|
888
|
+
meta: dict[str, Any] | None = None,
|
|
836
889
|
enabled: bool | None = None,
|
|
837
890
|
) -> Callable[[AnyFunction], FunctionTool]: ...
|
|
838
891
|
|
|
@@ -847,6 +900,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
847
900
|
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
848
901
|
annotations: ToolAnnotations | dict[str, Any] | None = None,
|
|
849
902
|
exclude_args: list[str] | None = None,
|
|
903
|
+
meta: dict[str, Any] | None = None,
|
|
850
904
|
enabled: bool | None = None,
|
|
851
905
|
) -> Callable[[AnyFunction], FunctionTool] | FunctionTool:
|
|
852
906
|
"""Decorator to register a tool.
|
|
@@ -870,6 +924,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
870
924
|
output_schema: Optional JSON schema for the tool's output
|
|
871
925
|
annotations: Optional annotations about the tool's behavior
|
|
872
926
|
exclude_args: Optional list of argument names to exclude from the tool schema
|
|
927
|
+
meta: Optional meta information about the tool
|
|
873
928
|
enabled: Optional boolean to enable or disable the tool
|
|
874
929
|
|
|
875
930
|
Examples:
|
|
@@ -928,6 +983,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
928
983
|
output_schema=output_schema,
|
|
929
984
|
annotations=annotations,
|
|
930
985
|
exclude_args=exclude_args,
|
|
986
|
+
meta=meta,
|
|
931
987
|
serializer=self._tool_serializer,
|
|
932
988
|
enabled=enabled,
|
|
933
989
|
)
|
|
@@ -960,6 +1016,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
960
1016
|
output_schema=output_schema,
|
|
961
1017
|
annotations=annotations,
|
|
962
1018
|
exclude_args=exclude_args,
|
|
1019
|
+
meta=meta,
|
|
963
1020
|
enabled=enabled,
|
|
964
1021
|
)
|
|
965
1022
|
|
|
@@ -973,7 +1030,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
973
1030
|
The resource instance that was added to the server.
|
|
974
1031
|
"""
|
|
975
1032
|
self._resource_manager.add_resource(resource)
|
|
976
|
-
self._cache.clear()
|
|
977
1033
|
|
|
978
1034
|
# Send notification if we're in a request context
|
|
979
1035
|
try:
|
|
@@ -1045,7 +1101,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1045
1101
|
mime_type=mime_type,
|
|
1046
1102
|
tags=tags,
|
|
1047
1103
|
)
|
|
1048
|
-
self._cache.clear()
|
|
1049
1104
|
|
|
1050
1105
|
def resource(
|
|
1051
1106
|
self,
|
|
@@ -1057,6 +1112,8 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1057
1112
|
mime_type: str | None = None,
|
|
1058
1113
|
tags: set[str] | None = None,
|
|
1059
1114
|
enabled: bool | None = None,
|
|
1115
|
+
annotations: Annotations | dict[str, Any] | None = None,
|
|
1116
|
+
meta: dict[str, Any] | None = None,
|
|
1060
1117
|
) -> Callable[[AnyFunction], Resource | ResourceTemplate]:
|
|
1061
1118
|
"""Decorator to register a function as a resource.
|
|
1062
1119
|
|
|
@@ -1080,6 +1137,8 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1080
1137
|
mime_type: Optional MIME type for the resource
|
|
1081
1138
|
tags: Optional set of tags for categorizing the resource
|
|
1082
1139
|
enabled: Optional boolean to enable or disable the resource
|
|
1140
|
+
annotations: Optional annotations about the resource's behavior
|
|
1141
|
+
meta: Optional meta information about the resource
|
|
1083
1142
|
|
|
1084
1143
|
Examples:
|
|
1085
1144
|
Register a resource with a custom name:
|
|
@@ -1108,6 +1167,9 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1108
1167
|
return f"Weather for {city}: {data}"
|
|
1109
1168
|
```
|
|
1110
1169
|
"""
|
|
1170
|
+
if isinstance(annotations, dict):
|
|
1171
|
+
annotations = Annotations(**annotations)
|
|
1172
|
+
|
|
1111
1173
|
# Check if user passed function directly instead of calling decorator
|
|
1112
1174
|
if inspect.isroutine(uri):
|
|
1113
1175
|
raise TypeError(
|
|
@@ -1149,6 +1211,8 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1149
1211
|
mime_type=mime_type,
|
|
1150
1212
|
tags=tags,
|
|
1151
1213
|
enabled=enabled,
|
|
1214
|
+
annotations=annotations,
|
|
1215
|
+
meta=meta,
|
|
1152
1216
|
)
|
|
1153
1217
|
self.add_template(template)
|
|
1154
1218
|
return template
|
|
@@ -1162,6 +1226,8 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1162
1226
|
mime_type=mime_type,
|
|
1163
1227
|
tags=tags,
|
|
1164
1228
|
enabled=enabled,
|
|
1229
|
+
annotations=annotations,
|
|
1230
|
+
meta=meta,
|
|
1165
1231
|
)
|
|
1166
1232
|
self.add_resource(resource)
|
|
1167
1233
|
return resource
|
|
@@ -1183,7 +1249,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1183
1249
|
The prompt instance that was added to the server.
|
|
1184
1250
|
"""
|
|
1185
1251
|
self._prompt_manager.add_prompt(prompt)
|
|
1186
|
-
self._cache.clear()
|
|
1187
1252
|
|
|
1188
1253
|
# Send notification if we're in a request context
|
|
1189
1254
|
try:
|
|
@@ -1206,6 +1271,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1206
1271
|
description: str | None = None,
|
|
1207
1272
|
tags: set[str] | None = None,
|
|
1208
1273
|
enabled: bool | None = None,
|
|
1274
|
+
meta: dict[str, Any] | None = None,
|
|
1209
1275
|
) -> FunctionPrompt: ...
|
|
1210
1276
|
|
|
1211
1277
|
@overload
|
|
@@ -1218,6 +1284,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1218
1284
|
description: str | None = None,
|
|
1219
1285
|
tags: set[str] | None = None,
|
|
1220
1286
|
enabled: bool | None = None,
|
|
1287
|
+
meta: dict[str, Any] | None = None,
|
|
1221
1288
|
) -> Callable[[AnyFunction], FunctionPrompt]: ...
|
|
1222
1289
|
|
|
1223
1290
|
def prompt(
|
|
@@ -1229,6 +1296,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1229
1296
|
description: str | None = None,
|
|
1230
1297
|
tags: set[str] | None = None,
|
|
1231
1298
|
enabled: bool | None = None,
|
|
1299
|
+
meta: dict[str, Any] | None = None,
|
|
1232
1300
|
) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt:
|
|
1233
1301
|
"""Decorator to register a prompt.
|
|
1234
1302
|
|
|
@@ -1249,6 +1317,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1249
1317
|
description: Optional description of what the prompt does
|
|
1250
1318
|
tags: Optional set of tags for categorizing the prompt
|
|
1251
1319
|
enabled: Optional boolean to enable or disable the prompt
|
|
1320
|
+
meta: Optional meta information about the prompt
|
|
1252
1321
|
|
|
1253
1322
|
Examples:
|
|
1254
1323
|
|
|
@@ -1326,6 +1395,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1326
1395
|
description=description,
|
|
1327
1396
|
tags=tags,
|
|
1328
1397
|
enabled=enabled,
|
|
1398
|
+
meta=meta,
|
|
1329
1399
|
)
|
|
1330
1400
|
self.add_prompt(prompt)
|
|
1331
1401
|
|
|
@@ -1355,6 +1425,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1355
1425
|
description=description,
|
|
1356
1426
|
tags=tags,
|
|
1357
1427
|
enabled=enabled,
|
|
1428
|
+
meta=meta,
|
|
1358
1429
|
)
|
|
1359
1430
|
|
|
1360
1431
|
async def run_stdio_async(self, show_banner: bool = True) -> None:
|
|
@@ -1726,8 +1797,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1726
1797
|
self._resource_manager.mount(mounted_server)
|
|
1727
1798
|
self._prompt_manager.mount(mounted_server)
|
|
1728
1799
|
|
|
1729
|
-
self._cache.clear()
|
|
1730
|
-
|
|
1731
1800
|
async def import_server(
|
|
1732
1801
|
self,
|
|
1733
1802
|
server: FastMCP[LifespanResultT],
|
|
@@ -1850,55 +1919,72 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1850
1919
|
else:
|
|
1851
1920
|
logger.debug(f"Imported server {server.name}")
|
|
1852
1921
|
|
|
1853
|
-
self._cache.clear()
|
|
1854
|
-
|
|
1855
1922
|
@classmethod
|
|
1856
1923
|
def from_openapi(
|
|
1857
1924
|
cls,
|
|
1858
1925
|
openapi_spec: dict[str, Any],
|
|
1859
1926
|
client: httpx.AsyncClient,
|
|
1860
|
-
route_maps: list[RouteMap] | None = None,
|
|
1861
|
-
route_map_fn: OpenAPIRouteMapFn | None = None,
|
|
1862
|
-
mcp_component_fn: OpenAPIComponentFn | None = None,
|
|
1927
|
+
route_maps: list[RouteMap] | list[RouteMapNew] | None = None,
|
|
1928
|
+
route_map_fn: OpenAPIRouteMapFn | OpenAPIRouteMapFnNew | None = None,
|
|
1929
|
+
mcp_component_fn: OpenAPIComponentFn | OpenAPIComponentFnNew | None = None,
|
|
1863
1930
|
mcp_names: dict[str, str] | None = None,
|
|
1864
1931
|
tags: set[str] | None = None,
|
|
1865
1932
|
**settings: Any,
|
|
1866
|
-
) -> FastMCPOpenAPI:
|
|
1933
|
+
) -> FastMCPOpenAPI | FastMCPOpenAPINew:
|
|
1867
1934
|
"""
|
|
1868
1935
|
Create a FastMCP server from an OpenAPI specification.
|
|
1869
1936
|
"""
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1937
|
+
|
|
1938
|
+
# Check if experimental parser is enabled
|
|
1939
|
+
if fastmcp.settings.experimental.enable_new_openapi_parser:
|
|
1940
|
+
from fastmcp.experimental.server.openapi import FastMCPOpenAPI
|
|
1941
|
+
|
|
1942
|
+
return FastMCPOpenAPI(
|
|
1943
|
+
openapi_spec=openapi_spec,
|
|
1944
|
+
client=client,
|
|
1945
|
+
route_maps=cast(Any, route_maps),
|
|
1946
|
+
route_map_fn=cast(Any, route_map_fn),
|
|
1947
|
+
mcp_component_fn=cast(Any, mcp_component_fn),
|
|
1948
|
+
mcp_names=mcp_names,
|
|
1949
|
+
tags=tags,
|
|
1950
|
+
**settings,
|
|
1951
|
+
)
|
|
1952
|
+
else:
|
|
1953
|
+
logger.info(
|
|
1954
|
+
"Using legacy OpenAPI parser. To use the new parser, set "
|
|
1955
|
+
"FASTMCP_EXPERIMENTAL_ENABLE_NEW_OPENAPI_PARSER=true. The new parser "
|
|
1956
|
+
"was introduced for testing in 2.11 and will become the default soon."
|
|
1957
|
+
)
|
|
1958
|
+
from .openapi import FastMCPOpenAPI
|
|
1959
|
+
|
|
1960
|
+
return FastMCPOpenAPI(
|
|
1961
|
+
openapi_spec=openapi_spec,
|
|
1962
|
+
client=client,
|
|
1963
|
+
route_maps=cast(Any, route_maps),
|
|
1964
|
+
route_map_fn=cast(Any, route_map_fn),
|
|
1965
|
+
mcp_component_fn=cast(Any, mcp_component_fn),
|
|
1966
|
+
mcp_names=mcp_names,
|
|
1967
|
+
tags=tags,
|
|
1968
|
+
**settings,
|
|
1969
|
+
)
|
|
1882
1970
|
|
|
1883
1971
|
@classmethod
|
|
1884
1972
|
def from_fastapi(
|
|
1885
1973
|
cls,
|
|
1886
1974
|
app: Any,
|
|
1887
1975
|
name: str | None = None,
|
|
1888
|
-
route_maps: list[RouteMap] | None = None,
|
|
1889
|
-
route_map_fn: OpenAPIRouteMapFn | None = None,
|
|
1890
|
-
mcp_component_fn: OpenAPIComponentFn | None = None,
|
|
1976
|
+
route_maps: list[RouteMap] | list[RouteMapNew] | None = None,
|
|
1977
|
+
route_map_fn: OpenAPIRouteMapFn | OpenAPIRouteMapFnNew | None = None,
|
|
1978
|
+
mcp_component_fn: OpenAPIComponentFn | OpenAPIComponentFnNew | None = None,
|
|
1891
1979
|
mcp_names: dict[str, str] | None = None,
|
|
1892
1980
|
httpx_client_kwargs: dict[str, Any] | None = None,
|
|
1893
1981
|
tags: set[str] | None = None,
|
|
1894
1982
|
**settings: Any,
|
|
1895
|
-
) -> FastMCPOpenAPI:
|
|
1983
|
+
) -> FastMCPOpenAPI | FastMCPOpenAPINew:
|
|
1896
1984
|
"""
|
|
1897
1985
|
Create a FastMCP server from a FastAPI application.
|
|
1898
1986
|
"""
|
|
1899
1987
|
|
|
1900
|
-
from .openapi import FastMCPOpenAPI
|
|
1901
|
-
|
|
1902
1988
|
if httpx_client_kwargs is None:
|
|
1903
1989
|
httpx_client_kwargs = {}
|
|
1904
1990
|
httpx_client_kwargs.setdefault("base_url", "http://fastapi")
|
|
@@ -1910,17 +1996,40 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1910
1996
|
|
|
1911
1997
|
name = name or app.title
|
|
1912
1998
|
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1999
|
+
# Check if experimental parser is enabled
|
|
2000
|
+
if fastmcp.settings.experimental.enable_new_openapi_parser:
|
|
2001
|
+
from fastmcp.experimental.server.openapi import FastMCPOpenAPI
|
|
2002
|
+
|
|
2003
|
+
return FastMCPOpenAPI(
|
|
2004
|
+
openapi_spec=app.openapi(),
|
|
2005
|
+
client=client,
|
|
2006
|
+
name=name,
|
|
2007
|
+
route_maps=cast(Any, route_maps),
|
|
2008
|
+
route_map_fn=cast(Any, route_map_fn),
|
|
2009
|
+
mcp_component_fn=cast(Any, mcp_component_fn),
|
|
2010
|
+
mcp_names=mcp_names,
|
|
2011
|
+
tags=tags,
|
|
2012
|
+
**settings,
|
|
2013
|
+
)
|
|
2014
|
+
else:
|
|
2015
|
+
logger.info(
|
|
2016
|
+
"Using legacy OpenAPI parser. To use the new parser, set "
|
|
2017
|
+
"FASTMCP_EXPERIMENTAL_ENABLE_NEW_OPENAPI_PARSER=true. The new parser "
|
|
2018
|
+
"was introduced for testing in 2.11 and will become the default soon."
|
|
2019
|
+
)
|
|
2020
|
+
from .openapi import FastMCPOpenAPI
|
|
2021
|
+
|
|
2022
|
+
return FastMCPOpenAPI(
|
|
2023
|
+
openapi_spec=app.openapi(),
|
|
2024
|
+
client=client,
|
|
2025
|
+
name=name,
|
|
2026
|
+
route_maps=cast(Any, route_maps),
|
|
2027
|
+
route_map_fn=cast(Any, route_map_fn),
|
|
2028
|
+
mcp_component_fn=cast(Any, mcp_component_fn),
|
|
2029
|
+
mcp_names=mcp_names,
|
|
2030
|
+
tags=tags,
|
|
2031
|
+
**settings,
|
|
2032
|
+
)
|
|
1924
2033
|
|
|
1925
2034
|
@classmethod
|
|
1926
2035
|
def as_proxy(
|
fastmcp/settings.py
CHANGED
|
@@ -55,6 +55,25 @@ class ExtendedSettingsConfigDict(SettingsConfigDict, total=False):
|
|
|
55
55
|
env_prefixes: list[str] | None
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
class ExperimentalSettings(BaseSettings):
|
|
59
|
+
model_config = SettingsConfigDict(
|
|
60
|
+
env_prefix="FASTMCP_EXPERIMENTAL_",
|
|
61
|
+
extra="ignore",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
enable_new_openapi_parser: Annotated[
|
|
65
|
+
bool,
|
|
66
|
+
Field(
|
|
67
|
+
description=inspect.cleandoc(
|
|
68
|
+
"""
|
|
69
|
+
Whether to use the new OpenAPI parser. This parser was introduced
|
|
70
|
+
for testing in 2.11 and will become the default soon.
|
|
71
|
+
"""
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
] = False
|
|
75
|
+
|
|
76
|
+
|
|
58
77
|
class Settings(BaseSettings):
|
|
59
78
|
"""FastMCP settings."""
|
|
60
79
|
|
|
@@ -64,8 +83,35 @@ class Settings(BaseSettings):
|
|
|
64
83
|
extra="ignore",
|
|
65
84
|
env_nested_delimiter="__",
|
|
66
85
|
nested_model_default_partial_update=True,
|
|
86
|
+
validate_assignment=True,
|
|
67
87
|
)
|
|
68
88
|
|
|
89
|
+
def get_setting(self, attr: str) -> Any:
|
|
90
|
+
"""
|
|
91
|
+
Get a setting. If the setting contains one or more `__`, it will be
|
|
92
|
+
treated as a nested setting.
|
|
93
|
+
"""
|
|
94
|
+
settings = self
|
|
95
|
+
while "__" in attr:
|
|
96
|
+
parent_attr, attr = attr.split("__", 1)
|
|
97
|
+
if not hasattr(settings, parent_attr):
|
|
98
|
+
raise AttributeError(f"Setting {parent_attr} does not exist.")
|
|
99
|
+
settings = getattr(settings, parent_attr)
|
|
100
|
+
return getattr(settings, attr)
|
|
101
|
+
|
|
102
|
+
def set_setting(self, attr: str, value: Any) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Set a setting. If the setting contains one or more `__`, it will be
|
|
105
|
+
treated as a nested setting.
|
|
106
|
+
"""
|
|
107
|
+
settings = self
|
|
108
|
+
while "__" in attr:
|
|
109
|
+
parent_attr, attr = attr.split("__", 1)
|
|
110
|
+
if not hasattr(settings, parent_attr):
|
|
111
|
+
raise AttributeError(f"Setting {parent_attr} does not exist.")
|
|
112
|
+
settings = getattr(settings, parent_attr)
|
|
113
|
+
setattr(settings, attr, value)
|
|
114
|
+
|
|
69
115
|
@classmethod
|
|
70
116
|
def settings_customise_sources(
|
|
71
117
|
cls,
|
|
@@ -109,6 +155,8 @@ class Settings(BaseSettings):
|
|
|
109
155
|
return v.upper()
|
|
110
156
|
return v
|
|
111
157
|
|
|
158
|
+
experimental: ExperimentalSettings = ExperimentalSettings()
|
|
159
|
+
|
|
112
160
|
enable_rich_tracebacks: Annotated[
|
|
113
161
|
bool,
|
|
114
162
|
Field(
|
|
@@ -211,19 +259,23 @@ class Settings(BaseSettings):
|
|
|
211
259
|
)
|
|
212
260
|
|
|
213
261
|
# Auth settings
|
|
214
|
-
|
|
215
|
-
|
|
262
|
+
server_auth: Annotated[
|
|
263
|
+
str | None,
|
|
216
264
|
Field(
|
|
217
265
|
description=inspect.cleandoc(
|
|
218
266
|
"""
|
|
219
|
-
Configure the authentication provider
|
|
220
|
-
|
|
221
|
-
|
|
267
|
+
Configure the authentication provider for the server. Auth
|
|
268
|
+
providers are registered with a specific key, and providing that
|
|
269
|
+
key here will cause the server to automatically configure the
|
|
270
|
+
provider from the environment.
|
|
222
271
|
|
|
223
272
|
If None, no automatic configuration will take place.
|
|
224
273
|
|
|
225
274
|
This setting is *always* overriden by any auth provider passed to the
|
|
226
275
|
FastMCP constructor.
|
|
276
|
+
|
|
277
|
+
Note that most auth providers require additional configuration
|
|
278
|
+
that must be provided via env vars.
|
|
227
279
|
"""
|
|
228
280
|
),
|
|
229
281
|
),
|
|
@@ -256,6 +308,21 @@ class Settings(BaseSettings):
|
|
|
256
308
|
),
|
|
257
309
|
] = None
|
|
258
310
|
|
|
311
|
+
include_fastmcp_meta: Annotated[
|
|
312
|
+
bool,
|
|
313
|
+
Field(
|
|
314
|
+
default=True,
|
|
315
|
+
description=inspect.cleandoc(
|
|
316
|
+
"""
|
|
317
|
+
Whether to include FastMCP meta in the server's MCP responses.
|
|
318
|
+
If True, a `_fastmcp` key will be added to the `meta` field of
|
|
319
|
+
all MCP component responses. This key will contain a dict of
|
|
320
|
+
various FastMCP-specific metadata, such as tags.
|
|
321
|
+
"""
|
|
322
|
+
),
|
|
323
|
+
),
|
|
324
|
+
] = True
|
|
325
|
+
|
|
259
326
|
|
|
260
327
|
def __getattr__(name: str):
|
|
261
328
|
"""
|
|
@@ -268,7 +335,7 @@ def __getattr__(name: str):
|
|
|
268
335
|
# Deprecated in 2.10.2
|
|
269
336
|
if settings.deprecation_warnings:
|
|
270
337
|
warnings.warn(
|
|
271
|
-
"`from fastmcp.settings import settings` is deprecated. use `
|
|
338
|
+
"`from fastmcp.settings import settings` is deprecated. use `fastmcp.settings` instead.",
|
|
272
339
|
DeprecationWarning,
|
|
273
340
|
stacklevel=2,
|
|
274
341
|
)
|