fastmcp 2.4.0__py3-none-any.whl → 2.5.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/client/client.py +23 -1
- fastmcp/client/transports.py +39 -18
- fastmcp/prompts/prompt.py +11 -4
- fastmcp/prompts/prompt_manager.py +25 -5
- fastmcp/resources/resource_manager.py +31 -5
- fastmcp/resources/template.py +10 -5
- fastmcp/server/context.py +46 -0
- fastmcp/server/dependencies.py +32 -0
- fastmcp/server/http.py +2 -0
- fastmcp/server/openapi.py +305 -64
- fastmcp/server/server.py +101 -49
- fastmcp/settings.py +30 -1
- fastmcp/tools/tool.py +5 -1
- fastmcp/tools/tool_manager.py +9 -2
- fastmcp/utilities/logging.py +6 -1
- fastmcp/utilities/mcp_config.py +4 -3
- {fastmcp-2.4.0.dist-info → fastmcp-2.5.1.dist-info}/METADATA +4 -4
- {fastmcp-2.4.0.dist-info → fastmcp-2.5.1.dist-info}/RECORD +21 -21
- {fastmcp-2.4.0.dist-info → fastmcp-2.5.1.dist-info}/WHEEL +0 -0
- {fastmcp-2.4.0.dist-info → fastmcp-2.5.1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.4.0.dist-info → fastmcp-2.5.1.dist-info}/licenses/LICENSE +0 -0
fastmcp/server/server.py
CHANGED
|
@@ -63,7 +63,9 @@ from fastmcp.utilities.mcp_config import MCPConfig
|
|
|
63
63
|
if TYPE_CHECKING:
|
|
64
64
|
from fastmcp.client import Client
|
|
65
65
|
from fastmcp.client.transports import ClientTransport
|
|
66
|
+
from fastmcp.server.openapi import ComponentFn as OpenAPIComponentFn
|
|
66
67
|
from fastmcp.server.openapi import FastMCPOpenAPI, RouteMap
|
|
68
|
+
from fastmcp.server.openapi import RouteMapFn as OpenAPIRouteMapFn
|
|
67
69
|
from fastmcp.server.proxy import FastMCPProxy
|
|
68
70
|
logger = get_logger(__name__)
|
|
69
71
|
|
|
@@ -125,6 +127,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
125
127
|
on_duplicate_resources: DuplicateBehavior | None = None,
|
|
126
128
|
on_duplicate_prompts: DuplicateBehavior | None = None,
|
|
127
129
|
resource_prefix_format: Literal["protocol", "path"] | None = None,
|
|
130
|
+
mask_error_details: bool | None = None,
|
|
128
131
|
**settings: Any,
|
|
129
132
|
):
|
|
130
133
|
if settings:
|
|
@@ -139,6 +142,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
139
142
|
)
|
|
140
143
|
self.settings = fastmcp.settings.ServerSettings(**settings)
|
|
141
144
|
|
|
145
|
+
# If mask_error_details is provided, override the settings value
|
|
146
|
+
if mask_error_details is not None:
|
|
147
|
+
self.settings.mask_error_details = mask_error_details
|
|
148
|
+
|
|
142
149
|
self.resource_prefix_format: Literal["protocol", "path"]
|
|
143
150
|
if resource_prefix_format is None:
|
|
144
151
|
self.resource_prefix_format = (
|
|
@@ -157,11 +164,16 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
157
164
|
self._tool_manager = ToolManager(
|
|
158
165
|
duplicate_behavior=on_duplicate_tools,
|
|
159
166
|
serializer=tool_serializer,
|
|
167
|
+
mask_error_details=self.settings.mask_error_details,
|
|
160
168
|
)
|
|
161
169
|
self._resource_manager = ResourceManager(
|
|
162
|
-
duplicate_behavior=on_duplicate_resources
|
|
170
|
+
duplicate_behavior=on_duplicate_resources,
|
|
171
|
+
mask_error_details=self.settings.mask_error_details,
|
|
172
|
+
)
|
|
173
|
+
self._prompt_manager = PromptManager(
|
|
174
|
+
duplicate_behavior=on_duplicate_prompts,
|
|
175
|
+
mask_error_details=self.settings.mask_error_details,
|
|
163
176
|
)
|
|
164
|
-
self._prompt_manager = PromptManager(duplicate_behavior=on_duplicate_prompts)
|
|
165
177
|
|
|
166
178
|
if lifespan is None:
|
|
167
179
|
self._has_lifespan = False
|
|
@@ -377,21 +389,30 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
377
389
|
async def _mcp_call_tool(
|
|
378
390
|
self, key: str, arguments: dict[str, Any]
|
|
379
391
|
) -> list[TextContent | ImageContent | EmbeddedResource]:
|
|
380
|
-
"""
|
|
392
|
+
"""Handle MCP 'callTool' requests.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
key: The name of the tool to call
|
|
396
|
+
arguments: Arguments to pass to the tool
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
List of MCP Content objects containing the tool results
|
|
400
|
+
"""
|
|
401
|
+
logger.debug("Call tool: %s with %s", key, arguments)
|
|
381
402
|
|
|
403
|
+
# Create and use context for the entire call
|
|
382
404
|
with fastmcp.server.context.Context(fastmcp=self):
|
|
405
|
+
# Get tool, checking first from our tools, then from the mounted servers
|
|
383
406
|
if self._tool_manager.has_tool(key):
|
|
384
|
-
|
|
407
|
+
return await self._tool_manager.call_tool(key, arguments)
|
|
385
408
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
raise NotFoundError(f"Unknown tool: {key}")
|
|
394
|
-
return result
|
|
409
|
+
# Check mounted servers to see if they have the tool
|
|
410
|
+
for server in self._mounted_servers.values():
|
|
411
|
+
if server.match_tool(key):
|
|
412
|
+
tool_key = server.strip_tool_prefix(key)
|
|
413
|
+
return await server.server._mcp_call_tool(tool_key, arguments)
|
|
414
|
+
|
|
415
|
+
raise NotFoundError(f"Unknown tool: {key}")
|
|
395
416
|
|
|
396
417
|
async def _mcp_read_resource(self, uri: AnyUrl | str) -> list[ReadResourceContents]:
|
|
397
418
|
"""
|
|
@@ -419,24 +440,30 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
419
440
|
async def _mcp_get_prompt(
|
|
420
441
|
self, name: str, arguments: dict[str, Any] | None = None
|
|
421
442
|
) -> GetPromptResult:
|
|
422
|
-
"""
|
|
423
|
-
|
|
424
|
-
|
|
443
|
+
"""Handle MCP 'getPrompt' requests.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
name: The name of the prompt to render
|
|
447
|
+
arguments: Arguments to pass to the prompt
|
|
425
448
|
|
|
449
|
+
Returns:
|
|
450
|
+
GetPromptResult containing the rendered prompt messages
|
|
426
451
|
"""
|
|
452
|
+
logger.debug("Get prompt: %s with %s", name, arguments)
|
|
453
|
+
|
|
454
|
+
# Create and use context for the entire call
|
|
427
455
|
with fastmcp.server.context.Context(fastmcp=self):
|
|
456
|
+
# Get prompt, checking first from our prompts, then from the mounted servers
|
|
428
457
|
if self._prompt_manager.has_prompt(name):
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
else:
|
|
439
|
-
raise NotFoundError(f"Unknown prompt: {name}")
|
|
458
|
+
return await self._prompt_manager.render_prompt(name, arguments)
|
|
459
|
+
|
|
460
|
+
# Check mounted servers to see if they have the prompt
|
|
461
|
+
for server in self._mounted_servers.values():
|
|
462
|
+
if server.match_prompt(name):
|
|
463
|
+
prompt_name = server.strip_prompt_prefix(name)
|
|
464
|
+
return await server.server._mcp_get_prompt(prompt_name, arguments)
|
|
465
|
+
|
|
466
|
+
raise NotFoundError(f"Unknown prompt: {name}")
|
|
440
467
|
|
|
441
468
|
def add_tool(
|
|
442
469
|
self,
|
|
@@ -854,7 +881,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
854
881
|
auth_server_provider=self._auth_server_provider,
|
|
855
882
|
auth_settings=self.settings.auth,
|
|
856
883
|
debug=self.settings.debug,
|
|
857
|
-
routes=self._additional_http_routes,
|
|
858
884
|
middleware=middleware,
|
|
859
885
|
)
|
|
860
886
|
|
|
@@ -905,7 +931,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
905
931
|
json_response=self.settings.json_response,
|
|
906
932
|
stateless_http=self.settings.stateless_http,
|
|
907
933
|
debug=self.settings.debug,
|
|
908
|
-
routes=self._additional_http_routes,
|
|
909
934
|
middleware=middleware,
|
|
910
935
|
)
|
|
911
936
|
elif transport == "sse":
|
|
@@ -916,7 +941,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
916
941
|
auth_server_provider=self._auth_server_provider,
|
|
917
942
|
auth_settings=self.settings.auth,
|
|
918
943
|
debug=self.settings.debug,
|
|
919
|
-
routes=self._additional_http_routes,
|
|
920
944
|
middleware=middleware,
|
|
921
945
|
)
|
|
922
946
|
|
|
@@ -1001,7 +1025,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1001
1025
|
from fastmcp.server.proxy import FastMCPProxy
|
|
1002
1026
|
|
|
1003
1027
|
if tool_separator is not None:
|
|
1004
|
-
# Deprecated since 2.
|
|
1028
|
+
# Deprecated since 2.4.0
|
|
1005
1029
|
warnings.warn(
|
|
1006
1030
|
"The tool_separator parameter is deprecated and will be removed in a future version. "
|
|
1007
1031
|
"Tools are now prefixed using 'prefix_toolname' format.",
|
|
@@ -1010,7 +1034,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1010
1034
|
)
|
|
1011
1035
|
|
|
1012
1036
|
if resource_separator is not None:
|
|
1013
|
-
# Deprecated since 2.
|
|
1037
|
+
# Deprecated since 2.4.0
|
|
1014
1038
|
warnings.warn(
|
|
1015
1039
|
"The resource_separator parameter is deprecated and ignored. "
|
|
1016
1040
|
"Resource prefixes are now added using the protocol://prefix/path format.",
|
|
@@ -1019,7 +1043,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1019
1043
|
)
|
|
1020
1044
|
|
|
1021
1045
|
if prompt_separator is not None:
|
|
1022
|
-
# Deprecated since 2.
|
|
1046
|
+
# Deprecated since 2.4.0
|
|
1023
1047
|
warnings.warn(
|
|
1024
1048
|
"The prompt_separator parameter is deprecated and will be removed in a future version. "
|
|
1025
1049
|
"Prompts are now prefixed using 'prefix_promptname' format.",
|
|
@@ -1086,7 +1110,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1086
1110
|
prompt_separator: Deprecated. Separator for prompt names.
|
|
1087
1111
|
"""
|
|
1088
1112
|
if tool_separator is not None:
|
|
1089
|
-
# Deprecated since 2.
|
|
1113
|
+
# Deprecated since 2.4.0
|
|
1090
1114
|
warnings.warn(
|
|
1091
1115
|
"The tool_separator parameter is deprecated and will be removed in a future version. "
|
|
1092
1116
|
"Tools are now prefixed using 'prefix_toolname' format.",
|
|
@@ -1095,7 +1119,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1095
1119
|
)
|
|
1096
1120
|
|
|
1097
1121
|
if resource_separator is not None:
|
|
1098
|
-
# Deprecated since 2.
|
|
1122
|
+
# Deprecated since 2.4.0
|
|
1099
1123
|
warnings.warn(
|
|
1100
1124
|
"The resource_separator parameter is deprecated and ignored. "
|
|
1101
1125
|
"Resource prefixes are now added using the protocol://prefix/path format.",
|
|
@@ -1104,7 +1128,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1104
1128
|
)
|
|
1105
1129
|
|
|
1106
1130
|
if prompt_separator is not None:
|
|
1107
|
-
# Deprecated since 2.
|
|
1131
|
+
# Deprecated since 2.4.0
|
|
1108
1132
|
warnings.warn(
|
|
1109
1133
|
"The prompt_separator parameter is deprecated and will be removed in a future version. "
|
|
1110
1134
|
"Prompts are now prefixed using 'prefix_promptname' format.",
|
|
@@ -1144,30 +1168,39 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1144
1168
|
openapi_spec: dict[str, Any],
|
|
1145
1169
|
client: httpx.AsyncClient,
|
|
1146
1170
|
route_maps: list[RouteMap] | None = None,
|
|
1171
|
+
route_map_fn: OpenAPIRouteMapFn | None = None,
|
|
1172
|
+
mcp_component_fn: OpenAPIComponentFn | None = None,
|
|
1173
|
+
mcp_names: dict[str, str] | None = None,
|
|
1147
1174
|
all_routes_as_tools: bool = False,
|
|
1148
1175
|
**settings: Any,
|
|
1149
1176
|
) -> FastMCPOpenAPI:
|
|
1150
1177
|
"""
|
|
1151
1178
|
Create a FastMCP server from an OpenAPI specification.
|
|
1152
1179
|
"""
|
|
1153
|
-
from .openapi import FastMCPOpenAPI,
|
|
1180
|
+
from .openapi import FastMCPOpenAPI, MCPType, RouteMap
|
|
1181
|
+
|
|
1182
|
+
# Deprecated since 2.5.0
|
|
1183
|
+
if all_routes_as_tools:
|
|
1184
|
+
warnings.warn(
|
|
1185
|
+
"The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. "
|
|
1186
|
+
'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.',
|
|
1187
|
+
DeprecationWarning,
|
|
1188
|
+
stacklevel=2,
|
|
1189
|
+
)
|
|
1154
1190
|
|
|
1155
1191
|
if all_routes_as_tools and route_maps:
|
|
1156
1192
|
raise ValueError("Cannot specify both all_routes_as_tools and route_maps")
|
|
1157
1193
|
|
|
1158
1194
|
elif all_routes_as_tools:
|
|
1159
|
-
route_maps = [
|
|
1160
|
-
RouteMap(
|
|
1161
|
-
methods="*",
|
|
1162
|
-
pattern=r".*",
|
|
1163
|
-
route_type=RouteType.TOOL,
|
|
1164
|
-
)
|
|
1165
|
-
]
|
|
1195
|
+
route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]
|
|
1166
1196
|
|
|
1167
1197
|
return FastMCPOpenAPI(
|
|
1168
1198
|
openapi_spec=openapi_spec,
|
|
1169
1199
|
client=client,
|
|
1170
1200
|
route_maps=route_maps,
|
|
1201
|
+
route_map_fn=route_map_fn,
|
|
1202
|
+
mcp_component_fn=mcp_component_fn,
|
|
1203
|
+
mcp_names=mcp_names,
|
|
1171
1204
|
**settings,
|
|
1172
1205
|
)
|
|
1173
1206
|
|
|
@@ -1177,25 +1210,41 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1177
1210
|
app: Any,
|
|
1178
1211
|
name: str | None = None,
|
|
1179
1212
|
route_maps: list[RouteMap] | None = None,
|
|
1213
|
+
route_map_fn: OpenAPIRouteMapFn | None = None,
|
|
1214
|
+
mcp_component_fn: OpenAPIComponentFn | None = None,
|
|
1215
|
+
mcp_names: dict[str, str] | None = None,
|
|
1180
1216
|
all_routes_as_tools: bool = False,
|
|
1217
|
+
httpx_client_kwargs: dict[str, Any] | None = None,
|
|
1181
1218
|
**settings: Any,
|
|
1182
1219
|
) -> FastMCPOpenAPI:
|
|
1183
1220
|
"""
|
|
1184
1221
|
Create a FastMCP server from a FastAPI application.
|
|
1185
1222
|
"""
|
|
1186
1223
|
|
|
1187
|
-
from .openapi import FastMCPOpenAPI,
|
|
1224
|
+
from .openapi import FastMCPOpenAPI, MCPType, RouteMap
|
|
1225
|
+
|
|
1226
|
+
# Deprecated since 2.5.0
|
|
1227
|
+
if all_routes_as_tools:
|
|
1228
|
+
warnings.warn(
|
|
1229
|
+
"The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. "
|
|
1230
|
+
'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.',
|
|
1231
|
+
DeprecationWarning,
|
|
1232
|
+
stacklevel=2,
|
|
1233
|
+
)
|
|
1188
1234
|
|
|
1189
1235
|
if all_routes_as_tools and route_maps:
|
|
1190
1236
|
raise ValueError("Cannot specify both all_routes_as_tools and route_maps")
|
|
1191
1237
|
|
|
1192
1238
|
elif all_routes_as_tools:
|
|
1193
|
-
route_maps = [
|
|
1194
|
-
|
|
1195
|
-
|
|
1239
|
+
route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]
|
|
1240
|
+
|
|
1241
|
+
if httpx_client_kwargs is None:
|
|
1242
|
+
httpx_client_kwargs = {}
|
|
1243
|
+
httpx_client_kwargs.setdefault("base_url", "http://fastapi")
|
|
1196
1244
|
|
|
1197
1245
|
client = httpx.AsyncClient(
|
|
1198
|
-
transport=httpx.ASGITransport(app=app),
|
|
1246
|
+
transport=httpx.ASGITransport(app=app),
|
|
1247
|
+
**httpx_client_kwargs,
|
|
1199
1248
|
)
|
|
1200
1249
|
|
|
1201
1250
|
name = name or app.title
|
|
@@ -1205,6 +1254,9 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1205
1254
|
client=client,
|
|
1206
1255
|
name=name,
|
|
1207
1256
|
route_maps=route_maps,
|
|
1257
|
+
route_map_fn=route_map_fn,
|
|
1258
|
+
mcp_component_fn=mcp_component_fn,
|
|
1259
|
+
mcp_names=mcp_names,
|
|
1208
1260
|
**settings,
|
|
1209
1261
|
)
|
|
1210
1262
|
|
fastmcp/settings.py
CHANGED
|
@@ -29,6 +29,16 @@ class Settings(BaseSettings):
|
|
|
29
29
|
|
|
30
30
|
test_mode: bool = False
|
|
31
31
|
log_level: LOG_LEVEL = "INFO"
|
|
32
|
+
enable_rich_tracebacks: Annotated[
|
|
33
|
+
bool,
|
|
34
|
+
Field(
|
|
35
|
+
description=inspect.cleandoc(
|
|
36
|
+
"""
|
|
37
|
+
If True, will use rich tracebacks for logging.
|
|
38
|
+
"""
|
|
39
|
+
)
|
|
40
|
+
),
|
|
41
|
+
] = True
|
|
32
42
|
|
|
33
43
|
client_raise_first_exceptiongroup_error: Annotated[
|
|
34
44
|
bool,
|
|
@@ -82,7 +92,9 @@ class Settings(BaseSettings):
|
|
|
82
92
|
"""Finalize the settings."""
|
|
83
93
|
from fastmcp.utilities.logging import configure_logging
|
|
84
94
|
|
|
85
|
-
configure_logging(
|
|
95
|
+
configure_logging(
|
|
96
|
+
self.log_level, enable_rich_tracebacks=self.enable_rich_tracebacks
|
|
97
|
+
)
|
|
86
98
|
|
|
87
99
|
return self
|
|
88
100
|
|
|
@@ -124,6 +136,23 @@ class ServerSettings(BaseSettings):
|
|
|
124
136
|
# prompt settings
|
|
125
137
|
on_duplicate_prompts: DuplicateBehavior = "warn"
|
|
126
138
|
|
|
139
|
+
# error handling
|
|
140
|
+
mask_error_details: Annotated[
|
|
141
|
+
bool,
|
|
142
|
+
Field(
|
|
143
|
+
default=False,
|
|
144
|
+
description=inspect.cleandoc(
|
|
145
|
+
"""
|
|
146
|
+
If True, error details from user-supplied functions (tool, resource, prompt)
|
|
147
|
+
will be masked before being sent to clients. Only error messages from explicitly
|
|
148
|
+
raised ToolError, ResourceError, or PromptError will be included in responses.
|
|
149
|
+
If False (default), all error details will be included in responses, but prefixed
|
|
150
|
+
with appropriate context.
|
|
151
|
+
"""
|
|
152
|
+
),
|
|
153
|
+
),
|
|
154
|
+
] = False
|
|
155
|
+
|
|
127
156
|
dependencies: Annotated[
|
|
128
157
|
list[str],
|
|
129
158
|
Field(
|
fastmcp/tools/tool.py
CHANGED
|
@@ -69,13 +69,17 @@ class Tool(BaseModel):
|
|
|
69
69
|
if param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
70
70
|
raise ValueError("Functions with **kwargs are not supported as tools")
|
|
71
71
|
|
|
72
|
-
func_name = name or fn.__name__
|
|
72
|
+
func_name = name or getattr(fn, "__name__", None) or fn.__class__.__name__
|
|
73
73
|
|
|
74
74
|
if func_name == "<lambda>":
|
|
75
75
|
raise ValueError("You must provide a name for lambda functions")
|
|
76
76
|
|
|
77
77
|
func_doc = description or fn.__doc__ or ""
|
|
78
78
|
|
|
79
|
+
# if the fn is a callable class, we need to get the __call__ method from here out
|
|
80
|
+
if not inspect.isroutine(fn):
|
|
81
|
+
fn = fn.__call__
|
|
82
|
+
|
|
79
83
|
type_adapter = get_cached_typeadapter(fn)
|
|
80
84
|
schema = type_adapter.json_schema()
|
|
81
85
|
|
fastmcp/tools/tool_manager.py
CHANGED
|
@@ -23,9 +23,11 @@ class ToolManager:
|
|
|
23
23
|
self,
|
|
24
24
|
duplicate_behavior: DuplicateBehavior | None = None,
|
|
25
25
|
serializer: Callable[[Any], str] | None = None,
|
|
26
|
+
mask_error_details: bool = False,
|
|
26
27
|
):
|
|
27
28
|
self._tools: dict[str, Tool] = {}
|
|
28
29
|
self._serializer = serializer
|
|
30
|
+
self.mask_error_details = mask_error_details
|
|
29
31
|
|
|
30
32
|
# Default to "warn" if None is provided
|
|
31
33
|
if duplicate_behavior is None:
|
|
@@ -124,7 +126,12 @@ class ToolManager:
|
|
|
124
126
|
logger.exception(f"Error calling tool {key!r}: {e}")
|
|
125
127
|
raise e
|
|
126
128
|
|
|
127
|
-
#
|
|
129
|
+
# Handle other exceptions
|
|
128
130
|
except Exception as e:
|
|
129
131
|
logger.exception(f"Error calling tool {key!r}: {e}")
|
|
130
|
-
|
|
132
|
+
if self.mask_error_details:
|
|
133
|
+
# Mask internal details
|
|
134
|
+
raise ToolError(f"Error calling tool {key!r}") from e
|
|
135
|
+
else:
|
|
136
|
+
# Include original error details
|
|
137
|
+
raise ToolError(f"Error calling tool {key!r}: {e}") from e
|
fastmcp/utilities/logging.py
CHANGED
|
@@ -22,6 +22,7 @@ def get_logger(name: str) -> logging.Logger:
|
|
|
22
22
|
def configure_logging(
|
|
23
23
|
level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | int = "INFO",
|
|
24
24
|
logger: logging.Logger | None = None,
|
|
25
|
+
enable_rich_tracebacks: bool = True,
|
|
25
26
|
) -> None:
|
|
26
27
|
"""
|
|
27
28
|
Configure logging for FastMCP.
|
|
@@ -30,11 +31,15 @@ def configure_logging(
|
|
|
30
31
|
logger: the logger to configure
|
|
31
32
|
level: the log level to use
|
|
32
33
|
"""
|
|
34
|
+
|
|
33
35
|
if logger is None:
|
|
34
36
|
logger = logging.getLogger("FastMCP")
|
|
35
37
|
|
|
36
38
|
# Only configure the FastMCP logger namespace
|
|
37
|
-
handler = RichHandler(
|
|
39
|
+
handler = RichHandler(
|
|
40
|
+
console=Console(stderr=True),
|
|
41
|
+
rich_tracebacks=enable_rich_tracebacks,
|
|
42
|
+
)
|
|
38
43
|
formatter = logging.Formatter("%(message)s")
|
|
39
44
|
handler.setFormatter(formatter)
|
|
40
45
|
|
fastmcp/utilities/mcp_config.py
CHANGED
|
@@ -32,11 +32,12 @@ def infer_transport_type_from_url(
|
|
|
32
32
|
return "streamable-http"
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
class
|
|
35
|
+
class StdioMCPServer(BaseModel):
|
|
36
36
|
command: str
|
|
37
37
|
args: list[str] = Field(default_factory=list)
|
|
38
38
|
env: dict[str, Any] = Field(default_factory=dict)
|
|
39
39
|
cwd: str | None = None
|
|
40
|
+
transport: Literal["stdio"] = "stdio"
|
|
40
41
|
|
|
41
42
|
def to_transport(self) -> StdioTransport:
|
|
42
43
|
from fastmcp.client.transports import StdioTransport
|
|
@@ -51,8 +52,8 @@ class LocalMCPServer(BaseModel):
|
|
|
51
52
|
|
|
52
53
|
class RemoteMCPServer(BaseModel):
|
|
53
54
|
url: str
|
|
54
|
-
transport: Literal["streamable-http", "sse", "http"] | None = None
|
|
55
55
|
headers: dict[str, str] = Field(default_factory=dict)
|
|
56
|
+
transport: Literal["streamable-http", "sse", "http"] | None = None
|
|
56
57
|
|
|
57
58
|
def to_transport(self) -> StreamableHttpTransport | SSETransport:
|
|
58
59
|
from fastmcp.client.transports import SSETransport, StreamableHttpTransport
|
|
@@ -69,7 +70,7 @@ class RemoteMCPServer(BaseModel):
|
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
class MCPConfig(BaseModel):
|
|
72
|
-
mcpServers: dict[str,
|
|
73
|
+
mcpServers: dict[str, StdioMCPServer | RemoteMCPServer]
|
|
73
74
|
|
|
74
75
|
@classmethod
|
|
75
76
|
def from_dict(cls, config: dict[str, Any]) -> MCPConfig:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastmcp
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.1
|
|
4
4
|
Summary: The fast, Pythonic way to build MCP servers.
|
|
5
5
|
Project-URL: Homepage, https://gofastmcp.com
|
|
6
6
|
Project-URL: Repository, https://github.com/jlowin/fastmcp
|
|
@@ -44,11 +44,11 @@ Description-Content-Type: text/markdown
|
|
|
44
44
|
> [!NOTE]
|
|
45
45
|
> #### FastMCP 2.0 & The Official MCP SDK
|
|
46
46
|
>
|
|
47
|
-
>
|
|
47
|
+
> FastMCP is the standard framework for building MCP servers and clients. FastMCP 1.0 was incorporated into the [official MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk).
|
|
48
48
|
>
|
|
49
|
-
> **
|
|
49
|
+
> **This is FastMCP 2.0,** the actively maintained version that significantly expands on 1.0's basic server-building capabilities by introducing full client support, server composition, OpenAPI/FastAPI integration, remote server proxying, built-in testing tools, and more.
|
|
50
50
|
>
|
|
51
|
-
> FastMCP 2.0 is the
|
|
51
|
+
> FastMCP 2.0 is the complete toolkit for modern AI applications. Ready to upgrade or get started? Follow the [installation instructions](https://gofastmcp.com/getting-started/installation), which include specific steps for upgrading from the official MCP SDK.
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
fastmcp/__init__.py,sha256=yTAqLZORsPqbr7AE0ayw6zIYBeMlxQlI-3HE2WqbvHk,435
|
|
2
2
|
fastmcp/exceptions.py,sha256=YvaKqOT3w0boXF9ylIoaSIzW9XiQ1qLFG1LZq6B60H8,680
|
|
3
3
|
fastmcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
fastmcp/settings.py,sha256=
|
|
4
|
+
fastmcp/settings.py,sha256=ES59HUoZGLCtBiCKjf4ioVXjPSZtKLJrXhBN4OH_1N4,5356
|
|
5
5
|
fastmcp/cli/__init__.py,sha256=Ii284TNoG5lxTP40ETMGhHEq3lQZWxu9m9JuU57kUpQ,87
|
|
6
6
|
fastmcp/cli/claude.py,sha256=IAlcZ4qZKBBj09jZUMEx7EANZE_IR3vcu7zOBJmMOuU,4567
|
|
7
7
|
fastmcp/cli/cli.py,sha256=eRZ4tpne7dj_rhjREwiNRN5i9A1T8-ptxg1lYaHfS5o,12401
|
|
8
8
|
fastmcp/cli/run.py,sha256=o7Ge6JZKXYwlY2vYdMNoVX8agBchAaeU_73iPndojIM,5351
|
|
9
9
|
fastmcp/client/__init__.py,sha256=Ri8GFHolIKOZnXaMzIc3VpkLcEqAmOoYGCKgmSk6NnE,550
|
|
10
10
|
fastmcp/client/base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
fastmcp/client/client.py,sha256=
|
|
11
|
+
fastmcp/client/client.py,sha256=QJsb8PHheUTn4UPG9kxzgn8M10g0cUkFttnGj-OgPdk,20847
|
|
12
12
|
fastmcp/client/logging.py,sha256=hOPRailZUp89RUck6V4HPaWVZinVrNY8HD4hD0dd-fE,822
|
|
13
13
|
fastmcp/client/progress.py,sha256=WjLLDbUKMsx8DK-fqO7AGsXb83ak-6BMrLvzzznGmcI,1043
|
|
14
14
|
fastmcp/client/roots.py,sha256=IxI_bHwHTmg6c2H-s1av1ZgrRnNDieHtYwdGFbzXT5c,2471
|
|
15
15
|
fastmcp/client/sampling.py,sha256=UlDHxnd6k_HoU8RA3ob0g8-e6haJBc9u27N_v291QoI,1698
|
|
16
|
-
fastmcp/client/transports.py,sha256=
|
|
16
|
+
fastmcp/client/transports.py,sha256=Ooh1YCYcdy61Qa4Ugl2wEWTc-uy05FBApoXofWdVpk4,24376
|
|
17
17
|
fastmcp/contrib/README.md,sha256=rKknYSI1T192UvSszqwwDlQ2eYQpxywrNTLoj177SYU,878
|
|
18
18
|
fastmcp/contrib/bulk_tool_caller/README.md,sha256=5aUUY1TSFKtz1pvTLSDqkUCkGkuqMfMZNsLeaNqEgAc,1960
|
|
19
19
|
fastmcp/contrib/bulk_tool_caller/__init__.py,sha256=xvGSSaUXTQrc31erBoi1Gh7BikgOliETDiYVTP3rLxY,75
|
|
@@ -26,35 +26,35 @@ fastmcp/contrib/mcp_mixin/mcp_mixin.py,sha256=cfIRbnSxsVzglTD-auyTE0izVQeHP7Oz18
|
|
|
26
26
|
fastmcp/low_level/README.md,sha256=IRvElvOOc_RLLsqbUm7e6VOEwrKHPJeox0pV7JVKHWw,106
|
|
27
27
|
fastmcp/low_level/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
fastmcp/prompts/__init__.py,sha256=An8uMBUh9Hrb7qqcn_5_Hent7IOeSh7EA2IUVsIrtHc,179
|
|
29
|
-
fastmcp/prompts/prompt.py,sha256=
|
|
30
|
-
fastmcp/prompts/prompt_manager.py,sha256=
|
|
29
|
+
fastmcp/prompts/prompt.py,sha256=_bMuLMSnkH_vJpPcf_b8HOUnMOsQJXZdtjZBoebzNjI,8249
|
|
30
|
+
fastmcp/prompts/prompt_manager.py,sha256=qptEhZHMwc8XxQd5lTQg8iIb5MiTZVsNaux_XLvQ0mw,3871
|
|
31
31
|
fastmcp/resources/__init__.py,sha256=t0x1j8lc74rjUKtXe9H5Gs4fpQt82K4NgBK6Y7A0xTg,467
|
|
32
32
|
fastmcp/resources/resource.py,sha256=Rx1My_fi1f-oqnQ9R_v7ejopAk4BJDfbB75-s4d31dM,2492
|
|
33
|
-
fastmcp/resources/resource_manager.py,sha256=
|
|
34
|
-
fastmcp/resources/template.py,sha256=
|
|
33
|
+
fastmcp/resources/resource_manager.py,sha256=nsgCR3lo9t4Q0QR6txPfAas2upqIb8P8ZlqWAfV9Qc0,11344
|
|
34
|
+
fastmcp/resources/template.py,sha256=mdejT0ofACYrn32Jw3wdJ7bJcVbW_4VMQEwMZLDR3zM,7529
|
|
35
35
|
fastmcp/resources/types.py,sha256=5fUFvzRlekNjtfihtq8S-fT0alKoNfclzrugqeM5JRE,6366
|
|
36
36
|
fastmcp/server/__init__.py,sha256=bMD4aQD4yJqLz7-mudoNsyeV8UgQfRAg3PRwPvwTEds,119
|
|
37
|
-
fastmcp/server/context.py,sha256=
|
|
38
|
-
fastmcp/server/dependencies.py,sha256=
|
|
39
|
-
fastmcp/server/http.py,sha256=
|
|
40
|
-
fastmcp/server/openapi.py,sha256=
|
|
37
|
+
fastmcp/server/context.py,sha256=yN1e0LsnCl7cEpr9WlbvFhSf8oE56kKb-20m8h2SsBY,10171
|
|
38
|
+
fastmcp/server/dependencies.py,sha256=A1A2dKAyZ2GcAA2RQ6KA5SaHuLU3LSbZaGkzncgcX2E,1722
|
|
39
|
+
fastmcp/server/http.py,sha256=wZWUrLvKITlvkxQoggJ9RyvynCUMEJqqMMsvX7Hmb9o,12807
|
|
40
|
+
fastmcp/server/openapi.py,sha256=9qXSuEl671sT1F7nSM3SiD5KANGqHUhiL1BBdCnuCcU,39153
|
|
41
41
|
fastmcp/server/proxy.py,sha256=mt3eM6TQWfnZD5XehmTXisskZ4CBbsWyjRPjprlTjBY,9653
|
|
42
|
-
fastmcp/server/server.py,sha256=
|
|
42
|
+
fastmcp/server/server.py,sha256=C0VEnHuSoYt-qef1jm4REPJkcM2stQ2Zp_rT_sELr2Y,57141
|
|
43
43
|
fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
|
|
44
|
-
fastmcp/tools/tool.py,sha256=
|
|
45
|
-
fastmcp/tools/tool_manager.py,sha256=
|
|
44
|
+
fastmcp/tools/tool.py,sha256=id2DCxPOtgFa-CRZvCBmCOz16nWyJXcq2ubIjmtOxPg,7803
|
|
45
|
+
fastmcp/tools/tool_manager.py,sha256=785vKYlJ9B2B5ThXFhuXYB4VNY4h0283-_AAdy1hEfk,4430
|
|
46
46
|
fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
|
|
47
47
|
fastmcp/utilities/cache.py,sha256=aV3oZ-ZhMgLSM9iAotlUlEy5jFvGXrVo0Y5Bj4PBtqY,707
|
|
48
48
|
fastmcp/utilities/decorators.py,sha256=AjhjsetQZF4YOPV5MTZmIxO21iFp_4fDIS3O2_KNCEg,2990
|
|
49
49
|
fastmcp/utilities/exceptions.py,sha256=Aax9K0larjzrrgJBS6o_PQwoIrvBvVwck2suZvgafXE,1359
|
|
50
50
|
fastmcp/utilities/json_schema.py,sha256=m65XU9lPq7pCxJ9vvCeGRl0HOFr6ArezvYpMBR6-gAg,3777
|
|
51
|
-
fastmcp/utilities/logging.py,sha256=
|
|
52
|
-
fastmcp/utilities/mcp_config.py,sha256=
|
|
51
|
+
fastmcp/utilities/logging.py,sha256=B1WNO-ZWFjd9wiFSh13YtW1hAKaNmbpscDZleIAhr-g,1317
|
|
52
|
+
fastmcp/utilities/mcp_config.py,sha256=_wY3peaFDEgyOBkJ_Tb8sETk3mtdwtw1053q7ry0za0,2169
|
|
53
53
|
fastmcp/utilities/openapi.py,sha256=QQos4vP59HQ8vPDTKftWOIVv_zmW30mNxYSXVU7JUbY,38441
|
|
54
54
|
fastmcp/utilities/tests.py,sha256=teyHcl3j7WGfYJ6m42VuQYB_IVpGvPdFqIpC-UxsN78,3369
|
|
55
55
|
fastmcp/utilities/types.py,sha256=6CcqAQ1QqCO2HGSFlPS6FO5JRWnacjCcO2-EhyEnZV0,4400
|
|
56
|
-
fastmcp-2.
|
|
57
|
-
fastmcp-2.
|
|
58
|
-
fastmcp-2.
|
|
59
|
-
fastmcp-2.
|
|
60
|
-
fastmcp-2.
|
|
56
|
+
fastmcp-2.5.1.dist-info/METADATA,sha256=_gulwPGdZ2oVtGOs186YsSYcJw0KChjfP1jngkHnbWk,16510
|
|
57
|
+
fastmcp-2.5.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
58
|
+
fastmcp-2.5.1.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
|
|
59
|
+
fastmcp-2.5.1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
60
|
+
fastmcp-2.5.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|