fastmcp 2.9.1__py3-none-any.whl → 2.10.0__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 +16 -1
- fastmcp/cli/run.py +4 -0
- fastmcp/client/auth/oauth.py +5 -82
- fastmcp/client/client.py +114 -24
- fastmcp/client/elicitation.py +63 -0
- fastmcp/client/transports.py +50 -36
- fastmcp/contrib/component_manager/README.md +170 -0
- fastmcp/contrib/component_manager/__init__.py +4 -0
- fastmcp/contrib/component_manager/component_manager.py +186 -0
- fastmcp/contrib/component_manager/component_service.py +225 -0
- fastmcp/contrib/component_manager/example.py +59 -0
- fastmcp/prompts/prompt.py +12 -4
- fastmcp/resources/resource.py +8 -3
- fastmcp/resources/template.py +5 -0
- fastmcp/server/auth/auth.py +15 -0
- fastmcp/server/auth/providers/bearer.py +41 -3
- fastmcp/server/auth/providers/bearer_env.py +4 -0
- fastmcp/server/auth/providers/in_memory.py +15 -0
- fastmcp/server/context.py +144 -4
- fastmcp/server/elicitation.py +160 -0
- fastmcp/server/http.py +1 -9
- fastmcp/server/low_level.py +4 -2
- fastmcp/server/middleware/__init__.py +14 -1
- fastmcp/server/middleware/logging.py +11 -0
- fastmcp/server/middleware/middleware.py +10 -6
- fastmcp/server/openapi.py +19 -77
- fastmcp/server/proxy.py +13 -6
- fastmcp/server/server.py +76 -11
- fastmcp/settings.py +0 -17
- fastmcp/tools/tool.py +209 -57
- fastmcp/tools/tool_manager.py +2 -3
- fastmcp/tools/tool_transform.py +125 -26
- fastmcp/utilities/cli.py +106 -0
- fastmcp/utilities/components.py +5 -1
- fastmcp/utilities/json_schema_type.py +648 -0
- fastmcp/utilities/openapi.py +69 -0
- fastmcp/utilities/types.py +50 -19
- {fastmcp-2.9.1.dist-info → fastmcp-2.10.0.dist-info}/METADATA +3 -2
- {fastmcp-2.9.1.dist-info → fastmcp-2.10.0.dist-info}/RECORD +42 -33
- {fastmcp-2.9.1.dist-info → fastmcp-2.10.0.dist-info}/WHEEL +0 -0
- {fastmcp-2.9.1.dist-info → fastmcp-2.10.0.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.9.1.dist-info → fastmcp-2.10.0.dist-info}/licenses/LICENSE +0 -0
fastmcp/server/server.py
CHANGED
|
@@ -26,6 +26,7 @@ from mcp.server.lowlevel.server import LifespanResultT, NotificationOptions
|
|
|
26
26
|
from mcp.server.stdio import stdio_server
|
|
27
27
|
from mcp.types import (
|
|
28
28
|
AnyFunction,
|
|
29
|
+
ContentBlock,
|
|
29
30
|
GetPromptResult,
|
|
30
31
|
ToolAnnotations,
|
|
31
32
|
)
|
|
@@ -57,12 +58,13 @@ from fastmcp.server.low_level import LowLevelServer
|
|
|
57
58
|
from fastmcp.server.middleware import Middleware, MiddlewareContext
|
|
58
59
|
from fastmcp.settings import Settings
|
|
59
60
|
from fastmcp.tools import ToolManager
|
|
60
|
-
from fastmcp.tools.tool import FunctionTool, Tool
|
|
61
|
+
from fastmcp.tools.tool import FunctionTool, Tool, ToolResult
|
|
61
62
|
from fastmcp.utilities.cache import TimedCache
|
|
63
|
+
from fastmcp.utilities.cli import print_server_banner
|
|
62
64
|
from fastmcp.utilities.components import FastMCPComponent
|
|
63
65
|
from fastmcp.utilities.logging import get_logger
|
|
64
66
|
from fastmcp.utilities.mcp_config import MCPConfig
|
|
65
|
-
from fastmcp.utilities.types import
|
|
67
|
+
from fastmcp.utilities.types import NotSet, NotSetT
|
|
66
68
|
|
|
67
69
|
if TYPE_CHECKING:
|
|
68
70
|
from fastmcp.client import Client
|
|
@@ -284,6 +286,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
284
286
|
async def run_async(
|
|
285
287
|
self,
|
|
286
288
|
transport: Transport | None = None,
|
|
289
|
+
show_banner: bool = True,
|
|
287
290
|
**transport_kwargs: Any,
|
|
288
291
|
) -> None:
|
|
289
292
|
"""Run the FastMCP server asynchronously.
|
|
@@ -297,15 +300,23 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
297
300
|
raise ValueError(f"Unknown transport: {transport}")
|
|
298
301
|
|
|
299
302
|
if transport == "stdio":
|
|
300
|
-
await self.run_stdio_async(
|
|
303
|
+
await self.run_stdio_async(
|
|
304
|
+
show_banner=show_banner,
|
|
305
|
+
**transport_kwargs,
|
|
306
|
+
)
|
|
301
307
|
elif transport in {"http", "sse", "streamable-http"}:
|
|
302
|
-
await self.run_http_async(
|
|
308
|
+
await self.run_http_async(
|
|
309
|
+
transport=transport,
|
|
310
|
+
show_banner=show_banner,
|
|
311
|
+
**transport_kwargs,
|
|
312
|
+
)
|
|
303
313
|
else:
|
|
304
314
|
raise ValueError(f"Unknown transport: {transport}")
|
|
305
315
|
|
|
306
316
|
def run(
|
|
307
317
|
self,
|
|
308
318
|
transport: Transport | None = None,
|
|
319
|
+
show_banner: bool = True,
|
|
309
320
|
**transport_kwargs: Any,
|
|
310
321
|
) -> None:
|
|
311
322
|
"""Run the FastMCP server. Note this is a synchronous function.
|
|
@@ -314,7 +325,14 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
314
325
|
transport: Transport protocol to use ("stdio", "sse", or "streamable-http")
|
|
315
326
|
"""
|
|
316
327
|
|
|
317
|
-
anyio.run(
|
|
328
|
+
anyio.run(
|
|
329
|
+
partial(
|
|
330
|
+
self.run_async,
|
|
331
|
+
transport,
|
|
332
|
+
show_banner=show_banner,
|
|
333
|
+
**transport_kwargs,
|
|
334
|
+
)
|
|
335
|
+
)
|
|
318
336
|
|
|
319
337
|
def _setup_handlers(self) -> None:
|
|
320
338
|
"""Set up core MCP protocol handlers."""
|
|
@@ -441,7 +459,6 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
441
459
|
"""
|
|
442
460
|
List all available tools, in the format expected by the low-level MCP
|
|
443
461
|
server.
|
|
444
|
-
|
|
445
462
|
"""
|
|
446
463
|
|
|
447
464
|
async def _handler(
|
|
@@ -593,7 +610,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
593
610
|
|
|
594
611
|
async def _mcp_call_tool(
|
|
595
612
|
self, key: str, arguments: dict[str, Any]
|
|
596
|
-
) -> list[
|
|
613
|
+
) -> list[ContentBlock] | tuple[list[ContentBlock], dict[str, Any]]:
|
|
597
614
|
"""
|
|
598
615
|
Handle MCP 'callTool' requests.
|
|
599
616
|
|
|
@@ -610,20 +627,21 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
610
627
|
|
|
611
628
|
async with fastmcp.server.context.Context(fastmcp=self):
|
|
612
629
|
try:
|
|
613
|
-
|
|
630
|
+
result = await self._call_tool(key, arguments)
|
|
631
|
+
return result.to_mcp_result()
|
|
614
632
|
except DisabledError:
|
|
615
633
|
raise NotFoundError(f"Unknown tool: {key}")
|
|
616
634
|
except NotFoundError:
|
|
617
635
|
raise NotFoundError(f"Unknown tool: {key}")
|
|
618
636
|
|
|
619
|
-
async def _call_tool(self, key: str, arguments: dict[str, Any]) ->
|
|
637
|
+
async def _call_tool(self, key: str, arguments: dict[str, Any]) -> ToolResult:
|
|
620
638
|
"""
|
|
621
639
|
Applies this server's middleware and delegates the filtered call to the manager.
|
|
622
640
|
"""
|
|
623
641
|
|
|
624
642
|
async def _handler(
|
|
625
643
|
context: MiddlewareContext[mcp.types.CallToolRequestParams],
|
|
626
|
-
) ->
|
|
644
|
+
) -> ToolResult:
|
|
627
645
|
tool = await self._tool_manager.get_tool(context.message.name)
|
|
628
646
|
if not self._should_enable_component(tool):
|
|
629
647
|
raise NotFoundError(f"Unknown tool: {context.message.name!r}")
|
|
@@ -789,8 +807,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
789
807
|
name_or_fn: AnyFunction,
|
|
790
808
|
*,
|
|
791
809
|
name: str | None = None,
|
|
810
|
+
title: str | None = None,
|
|
792
811
|
description: str | None = None,
|
|
793
812
|
tags: set[str] | None = None,
|
|
813
|
+
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
794
814
|
annotations: ToolAnnotations | dict[str, Any] | None = None,
|
|
795
815
|
exclude_args: list[str] | None = None,
|
|
796
816
|
enabled: bool | None = None,
|
|
@@ -802,8 +822,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
802
822
|
name_or_fn: str | None = None,
|
|
803
823
|
*,
|
|
804
824
|
name: str | None = None,
|
|
825
|
+
title: str | None = None,
|
|
805
826
|
description: str | None = None,
|
|
806
827
|
tags: set[str] | None = None,
|
|
828
|
+
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
807
829
|
annotations: ToolAnnotations | dict[str, Any] | None = None,
|
|
808
830
|
exclude_args: list[str] | None = None,
|
|
809
831
|
enabled: bool | None = None,
|
|
@@ -814,8 +836,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
814
836
|
name_or_fn: str | AnyFunction | None = None,
|
|
815
837
|
*,
|
|
816
838
|
name: str | None = None,
|
|
839
|
+
title: str | None = None,
|
|
817
840
|
description: str | None = None,
|
|
818
841
|
tags: set[str] | None = None,
|
|
842
|
+
output_schema: dict[str, Any] | None | NotSetT = NotSet,
|
|
819
843
|
annotations: ToolAnnotations | dict[str, Any] | None = None,
|
|
820
844
|
exclude_args: list[str] | None = None,
|
|
821
845
|
enabled: bool | None = None,
|
|
@@ -838,6 +862,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
838
862
|
name: Optional name for the tool (keyword-only, alternative to name_or_fn)
|
|
839
863
|
description: Optional description of what the tool does
|
|
840
864
|
tags: Optional set of tags for categorizing the tool
|
|
865
|
+
output_schema: Optional JSON schema for the tool's output
|
|
841
866
|
annotations: Optional annotations about the tool's behavior
|
|
842
867
|
exclude_args: Optional list of argument names to exclude from the tool schema
|
|
843
868
|
enabled: Optional boolean to enable or disable the tool
|
|
@@ -892,8 +917,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
892
917
|
tool = Tool.from_function(
|
|
893
918
|
fn,
|
|
894
919
|
name=tool_name,
|
|
920
|
+
title=title,
|
|
895
921
|
description=description,
|
|
896
922
|
tags=tags,
|
|
923
|
+
output_schema=output_schema,
|
|
897
924
|
annotations=annotations,
|
|
898
925
|
exclude_args=exclude_args,
|
|
899
926
|
serializer=self._tool_serializer,
|
|
@@ -922,8 +949,10 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
922
949
|
return partial(
|
|
923
950
|
self.tool,
|
|
924
951
|
name=tool_name,
|
|
952
|
+
title=title,
|
|
925
953
|
description=description,
|
|
926
954
|
tags=tags,
|
|
955
|
+
output_schema=output_schema,
|
|
927
956
|
annotations=annotations,
|
|
928
957
|
exclude_args=exclude_args,
|
|
929
958
|
enabled=enabled,
|
|
@@ -1009,6 +1038,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1009
1038
|
uri: str,
|
|
1010
1039
|
*,
|
|
1011
1040
|
name: str | None = None,
|
|
1041
|
+
title: str | None = None,
|
|
1012
1042
|
description: str | None = None,
|
|
1013
1043
|
mime_type: str | None = None,
|
|
1014
1044
|
tags: set[str] | None = None,
|
|
@@ -1100,6 +1130,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1100
1130
|
fn=fn,
|
|
1101
1131
|
uri_template=uri,
|
|
1102
1132
|
name=name,
|
|
1133
|
+
title=title,
|
|
1103
1134
|
description=description,
|
|
1104
1135
|
mime_type=mime_type,
|
|
1105
1136
|
tags=tags,
|
|
@@ -1112,6 +1143,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1112
1143
|
fn=fn,
|
|
1113
1144
|
uri=uri,
|
|
1114
1145
|
name=name,
|
|
1146
|
+
title=title,
|
|
1115
1147
|
description=description,
|
|
1116
1148
|
mime_type=mime_type,
|
|
1117
1149
|
tags=tags,
|
|
@@ -1151,6 +1183,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1151
1183
|
name_or_fn: AnyFunction,
|
|
1152
1184
|
*,
|
|
1153
1185
|
name: str | None = None,
|
|
1186
|
+
title: str | None = None,
|
|
1154
1187
|
description: str | None = None,
|
|
1155
1188
|
tags: set[str] | None = None,
|
|
1156
1189
|
enabled: bool | None = None,
|
|
@@ -1162,6 +1195,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1162
1195
|
name_or_fn: str | None = None,
|
|
1163
1196
|
*,
|
|
1164
1197
|
name: str | None = None,
|
|
1198
|
+
title: str | None = None,
|
|
1165
1199
|
description: str | None = None,
|
|
1166
1200
|
tags: set[str] | None = None,
|
|
1167
1201
|
enabled: bool | None = None,
|
|
@@ -1172,6 +1206,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1172
1206
|
name_or_fn: str | AnyFunction | None = None,
|
|
1173
1207
|
*,
|
|
1174
1208
|
name: str | None = None,
|
|
1209
|
+
title: str | None = None,
|
|
1175
1210
|
description: str | None = None,
|
|
1176
1211
|
tags: set[str] | None = None,
|
|
1177
1212
|
enabled: bool | None = None,
|
|
@@ -1268,6 +1303,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1268
1303
|
prompt = Prompt.from_function(
|
|
1269
1304
|
fn=fn,
|
|
1270
1305
|
name=prompt_name,
|
|
1306
|
+
title=title,
|
|
1271
1307
|
description=description,
|
|
1272
1308
|
tags=tags,
|
|
1273
1309
|
enabled=enabled,
|
|
@@ -1296,13 +1332,22 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1296
1332
|
return partial(
|
|
1297
1333
|
self.prompt,
|
|
1298
1334
|
name=prompt_name,
|
|
1335
|
+
title=title,
|
|
1299
1336
|
description=description,
|
|
1300
1337
|
tags=tags,
|
|
1301
1338
|
enabled=enabled,
|
|
1302
1339
|
)
|
|
1303
1340
|
|
|
1304
|
-
async def run_stdio_async(self) -> None:
|
|
1341
|
+
async def run_stdio_async(self, show_banner: bool = True) -> None:
|
|
1305
1342
|
"""Run the server using stdio transport."""
|
|
1343
|
+
|
|
1344
|
+
# Display server banner
|
|
1345
|
+
if show_banner:
|
|
1346
|
+
print_server_banner(
|
|
1347
|
+
server=self,
|
|
1348
|
+
transport="stdio",
|
|
1349
|
+
)
|
|
1350
|
+
|
|
1306
1351
|
async with stdio_server() as (read_stream, write_stream):
|
|
1307
1352
|
logger.info(f"Starting MCP server {self.name!r} with transport 'stdio'")
|
|
1308
1353
|
await self._mcp_server.run(
|
|
@@ -1315,6 +1360,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1315
1360
|
|
|
1316
1361
|
async def run_http_async(
|
|
1317
1362
|
self,
|
|
1363
|
+
show_banner: bool = True,
|
|
1318
1364
|
transport: Literal["http", "streamable-http", "sse"] = "http",
|
|
1319
1365
|
host: str | None = None,
|
|
1320
1366
|
port: int | None = None,
|
|
@@ -1333,6 +1379,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1333
1379
|
path: Path for the endpoint (defaults to settings.streamable_http_path or settings.sse_path)
|
|
1334
1380
|
uvicorn_config: Additional configuration for the Uvicorn server
|
|
1335
1381
|
"""
|
|
1382
|
+
|
|
1336
1383
|
host = host or self._deprecated_settings.host
|
|
1337
1384
|
port = port or self._deprecated_settings.port
|
|
1338
1385
|
default_log_level_to_use = (
|
|
@@ -1341,6 +1388,23 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1341
1388
|
|
|
1342
1389
|
app = self.http_app(path=path, transport=transport, middleware=middleware)
|
|
1343
1390
|
|
|
1391
|
+
# Get the path for the server URL
|
|
1392
|
+
server_path = (
|
|
1393
|
+
app.state.path.lstrip("/")
|
|
1394
|
+
if hasattr(app, "state") and hasattr(app.state, "path")
|
|
1395
|
+
else path or ""
|
|
1396
|
+
)
|
|
1397
|
+
|
|
1398
|
+
# Display server banner
|
|
1399
|
+
if show_banner:
|
|
1400
|
+
print_server_banner(
|
|
1401
|
+
server=self,
|
|
1402
|
+
transport=transport,
|
|
1403
|
+
host=host,
|
|
1404
|
+
port=port,
|
|
1405
|
+
path=server_path,
|
|
1406
|
+
)
|
|
1407
|
+
|
|
1344
1408
|
_uvicorn_config_from_user = uvicorn_config or {}
|
|
1345
1409
|
|
|
1346
1410
|
config_kwargs: dict[str, Any] = {
|
|
@@ -1358,6 +1422,7 @@ class FastMCP(Generic[LifespanResultT]):
|
|
|
1358
1422
|
logger.info(
|
|
1359
1423
|
f"Starting MCP server {self.name!r} with transport {transport!r} on http://{host}:{port}/{path}"
|
|
1360
1424
|
)
|
|
1425
|
+
|
|
1361
1426
|
await server.serve()
|
|
1362
1427
|
|
|
1363
1428
|
async def run_sse_async(
|
fastmcp/settings.py
CHANGED
|
@@ -154,23 +154,6 @@ class Settings(BaseSettings):
|
|
|
154
154
|
),
|
|
155
155
|
] = "path"
|
|
156
156
|
|
|
157
|
-
tool_attempt_parse_json_args: Annotated[
|
|
158
|
-
bool,
|
|
159
|
-
Field(
|
|
160
|
-
default=False,
|
|
161
|
-
description=inspect.cleandoc(
|
|
162
|
-
"""
|
|
163
|
-
Note: this enables a legacy behavior. If True, will attempt to parse
|
|
164
|
-
stringified JSON lists and objects strings in tool arguments before
|
|
165
|
-
passing them to the tool. This is an old behavior that can create
|
|
166
|
-
unexpected type coercion issues, but may be helpful for less powerful
|
|
167
|
-
LLMs that stringify JSON instead of passing actual lists and objects.
|
|
168
|
-
Defaults to False.
|
|
169
|
-
"""
|
|
170
|
-
),
|
|
171
|
-
),
|
|
172
|
-
] = False
|
|
173
|
-
|
|
174
157
|
client_init_timeout: Annotated[
|
|
175
158
|
float | None,
|
|
176
159
|
Field(
|