fastmcp 2.10.1__py3-none-any.whl → 2.10.3__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 +117 -225
- fastmcp/cli/install/__init__.py +20 -0
- fastmcp/cli/install/claude_code.py +186 -0
- fastmcp/cli/install/claude_desktop.py +186 -0
- fastmcp/cli/install/cursor.py +196 -0
- fastmcp/cli/install/mcp_config.py +165 -0
- fastmcp/cli/install/shared.py +85 -0
- fastmcp/cli/run.py +17 -4
- fastmcp/client/client.py +230 -124
- fastmcp/client/elicitation.py +5 -0
- fastmcp/client/transports.py +3 -5
- fastmcp/mcp_config.py +282 -0
- fastmcp/prompts/prompt.py +2 -4
- fastmcp/resources/resource.py +2 -2
- fastmcp/resources/template.py +1 -1
- fastmcp/server/auth/providers/bearer.py +15 -6
- fastmcp/server/openapi.py +40 -9
- fastmcp/server/proxy.py +206 -37
- fastmcp/server/server.py +102 -20
- fastmcp/settings.py +19 -1
- fastmcp/tools/tool.py +3 -2
- fastmcp/tools/tool_transform.py +5 -6
- fastmcp/utilities/cli.py +102 -0
- fastmcp/utilities/json_schema.py +14 -3
- fastmcp/utilities/logging.py +3 -0
- fastmcp/utilities/openapi.py +92 -0
- fastmcp/utilities/tests.py +13 -0
- fastmcp/utilities/types.py +4 -3
- {fastmcp-2.10.1.dist-info → fastmcp-2.10.3.dist-info}/METADATA +4 -3
- {fastmcp-2.10.1.dist-info → fastmcp-2.10.3.dist-info}/RECORD +33 -26
- fastmcp/utilities/mcp_config.py +0 -93
- {fastmcp-2.10.1.dist-info → fastmcp-2.10.3.dist-info}/WHEEL +0 -0
- {fastmcp-2.10.1.dist-info → fastmcp-2.10.3.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.10.1.dist-info → fastmcp-2.10.3.dist-info}/licenses/LICENSE +0 -0
fastmcp/tools/tool_transform.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Any, Literal
|
|
|
9
9
|
from mcp.types import ToolAnnotations
|
|
10
10
|
from pydantic import ConfigDict
|
|
11
11
|
|
|
12
|
-
from fastmcp.tools.tool import ParsedFunction, Tool, ToolResult
|
|
12
|
+
from fastmcp.tools.tool import ParsedFunction, Tool, ToolResult, _convert_to_content
|
|
13
13
|
from fastmcp.utilities.logging import get_logger
|
|
14
14
|
from fastmcp.utilities.types import NotSet, NotSetT, get_cached_typeadapter
|
|
15
15
|
|
|
@@ -233,7 +233,6 @@ class TransformedTool(Tool):
|
|
|
233
233
|
Returns:
|
|
234
234
|
ToolResult object containing content and optional structured output.
|
|
235
235
|
"""
|
|
236
|
-
from fastmcp.tools.tool import _convert_to_content
|
|
237
236
|
|
|
238
237
|
# Fill in missing arguments with schema defaults to ensure
|
|
239
238
|
# ArgTransform defaults take precedence over function defaults
|
|
@@ -274,7 +273,6 @@ class TransformedTool(Tool):
|
|
|
274
273
|
if isinstance(result, ToolResult):
|
|
275
274
|
if self.output_schema is None:
|
|
276
275
|
# Check if this is from a custom function that returns ToolResult
|
|
277
|
-
import inspect
|
|
278
276
|
|
|
279
277
|
return_annotation = inspect.signature(self.fn).return_annotation
|
|
280
278
|
if return_annotation is ToolResult:
|
|
@@ -298,7 +296,6 @@ class TransformedTool(Tool):
|
|
|
298
296
|
return result
|
|
299
297
|
|
|
300
298
|
# Otherwise convert to content and create ToolResult with proper structured content
|
|
301
|
-
from fastmcp.tools.tool import _convert_to_content
|
|
302
299
|
|
|
303
300
|
unstructured_result = _convert_to_content(
|
|
304
301
|
result, serializer=self.serializer
|
|
@@ -433,8 +430,6 @@ class TransformedTool(Tool):
|
|
|
433
430
|
final_output_schema = parsed_fn.output_schema
|
|
434
431
|
if final_output_schema is None:
|
|
435
432
|
# Check if function returns ToolResult - if so, don't fall back to parent
|
|
436
|
-
import inspect
|
|
437
|
-
|
|
438
433
|
return_annotation = inspect.signature(
|
|
439
434
|
transform_fn
|
|
440
435
|
).return_annotation
|
|
@@ -553,6 +548,7 @@ class TransformedTool(Tool):
|
|
|
553
548
|
"""
|
|
554
549
|
|
|
555
550
|
# Build transformed schema and mapping
|
|
551
|
+
parent_defs = parent_tool.parameters.get("$defs", {})
|
|
556
552
|
parent_props = parent_tool.parameters.get("properties", {}).copy()
|
|
557
553
|
parent_required = set(parent_tool.parameters.get("required", []))
|
|
558
554
|
|
|
@@ -608,6 +604,9 @@ class TransformedTool(Tool):
|
|
|
608
604
|
"required": list(new_required),
|
|
609
605
|
}
|
|
610
606
|
|
|
607
|
+
if parent_defs:
|
|
608
|
+
schema["$defs"] = parent_defs
|
|
609
|
+
|
|
611
610
|
# Create forwarding function that closes over everything it needs
|
|
612
611
|
async def _forward(**kwargs):
|
|
613
612
|
# Validate arguments
|
fastmcp/utilities/cli.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import version
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
5
|
+
|
|
6
|
+
from rich.console import Console, Group
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
11
|
+
import fastmcp
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from fastmcp import FastMCP
|
|
15
|
+
|
|
16
|
+
LOGO_ASCII = r"""
|
|
17
|
+
_ __ ___ ______ __ __ _____________ ____ ____
|
|
18
|
+
_ __ ___ / ____/___ ______/ /_/ |/ / ____/ __ \ |___ \ / __ \
|
|
19
|
+
_ __ ___ / /_ / __ `/ ___/ __/ /|_/ / / / /_/ / ___/ / / / / /
|
|
20
|
+
_ __ ___ / __/ / /_/ (__ ) /_/ / / / /___/ ____/ / __/_/ /_/ /
|
|
21
|
+
_ __ ___ /_/ \__,_/____/\__/_/ /_/\____/_/ /_____(_)____/
|
|
22
|
+
|
|
23
|
+
""".lstrip("\n")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def log_server_banner(
|
|
27
|
+
server: FastMCP[Any],
|
|
28
|
+
transport: Literal["stdio", "http", "sse", "streamable-http"],
|
|
29
|
+
*,
|
|
30
|
+
host: str | None = None,
|
|
31
|
+
port: int | None = None,
|
|
32
|
+
path: str | None = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Creates and logs a formatted banner with server information and logo.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
transport: The transport protocol being used
|
|
38
|
+
server_name: Optional server name to display
|
|
39
|
+
host: Host address (for HTTP transports)
|
|
40
|
+
port: Port number (for HTTP transports)
|
|
41
|
+
path: Server path (for HTTP transports)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
# Create the logo text
|
|
45
|
+
logo_text = Text(LOGO_ASCII, style="bold green")
|
|
46
|
+
|
|
47
|
+
# Create the information table
|
|
48
|
+
info_table = Table.grid(padding=(0, 1))
|
|
49
|
+
info_table.add_column(style="bold", justify="center") # Emoji column
|
|
50
|
+
info_table.add_column(style="bold cyan", justify="left") # Label column
|
|
51
|
+
info_table.add_column(style="white", justify="left") # Value column
|
|
52
|
+
|
|
53
|
+
match transport:
|
|
54
|
+
case "http" | "streamable-http":
|
|
55
|
+
display_transport = "Streamable-HTTP"
|
|
56
|
+
case "sse":
|
|
57
|
+
display_transport = "SSE"
|
|
58
|
+
case "stdio":
|
|
59
|
+
display_transport = "STDIO"
|
|
60
|
+
|
|
61
|
+
info_table.add_row("🖥️", "Server name:", server.name)
|
|
62
|
+
info_table.add_row("📦", "Transport:", display_transport)
|
|
63
|
+
|
|
64
|
+
# Show connection info based on transport
|
|
65
|
+
if transport in ("http", "streamable-http", "sse"):
|
|
66
|
+
if host and port:
|
|
67
|
+
server_url = f"http://{host}:{port}"
|
|
68
|
+
if path:
|
|
69
|
+
server_url += f"/{path.lstrip('/')}"
|
|
70
|
+
info_table.add_row("🔗", "Server URL:", server_url)
|
|
71
|
+
|
|
72
|
+
# Add documentation link
|
|
73
|
+
info_table.add_row("", "", "")
|
|
74
|
+
info_table.add_row("📚", "Docs:", "https://gofastmcp.com")
|
|
75
|
+
info_table.add_row("🚀", "Deploy:", "https://fastmcp.cloud")
|
|
76
|
+
|
|
77
|
+
# Add version information with explicit style overrides
|
|
78
|
+
info_table.add_row("", "", "")
|
|
79
|
+
info_table.add_row(
|
|
80
|
+
"🏎️",
|
|
81
|
+
"FastMCP version:",
|
|
82
|
+
Text(fastmcp.__version__, style="dim white", no_wrap=True),
|
|
83
|
+
)
|
|
84
|
+
info_table.add_row(
|
|
85
|
+
"🤝",
|
|
86
|
+
"MCP version:",
|
|
87
|
+
Text(version("mcp"), style="dim white", no_wrap=True),
|
|
88
|
+
)
|
|
89
|
+
# Create panel with logo and information using Group
|
|
90
|
+
panel_content = Group(logo_text, "", info_table)
|
|
91
|
+
|
|
92
|
+
panel = Panel(
|
|
93
|
+
panel_content,
|
|
94
|
+
title="FastMCP 2.0",
|
|
95
|
+
title_align="left",
|
|
96
|
+
border_style="dim",
|
|
97
|
+
padding=(2, 5),
|
|
98
|
+
expand=False,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
console = Console(stderr=True)
|
|
102
|
+
console.print(Group("\n", panel, "\n"))
|
fastmcp/utilities/json_schema.py
CHANGED
|
@@ -67,13 +67,24 @@ def _prune_unused_defs(schema: dict) -> dict:
|
|
|
67
67
|
walk(value, current_def=def_name)
|
|
68
68
|
|
|
69
69
|
# Figure out what defs were referenced directly or recursively
|
|
70
|
-
def def_is_referenced(def_name):
|
|
70
|
+
def def_is_referenced(def_name, parent_def_names: set[str] | None = None):
|
|
71
71
|
if def_name in root_defs:
|
|
72
72
|
return True
|
|
73
73
|
references = referenced_by.get(def_name)
|
|
74
74
|
if references:
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
if parent_def_names is None:
|
|
76
|
+
parent_def_names = set()
|
|
77
|
+
|
|
78
|
+
# Handle recursion by excluding references already present in parent references
|
|
79
|
+
parent_def_names = parent_def_names | {def_name}
|
|
80
|
+
valid_references = [
|
|
81
|
+
reference
|
|
82
|
+
for reference in references
|
|
83
|
+
if reference not in parent_def_names
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
for reference in valid_references:
|
|
87
|
+
if def_is_referenced(reference, parent_def_names):
|
|
77
88
|
return True
|
|
78
89
|
return False
|
|
79
90
|
|
fastmcp/utilities/logging.py
CHANGED
fastmcp/utilities/openapi.py
CHANGED
|
@@ -152,6 +152,7 @@ __all__ = [
|
|
|
152
152
|
"ParameterLocation",
|
|
153
153
|
"JsonSchema",
|
|
154
154
|
"parse_openapi_to_http_routes",
|
|
155
|
+
"extract_output_schema_from_responses",
|
|
155
156
|
]
|
|
156
157
|
|
|
157
158
|
# Type variables for generic parser
|
|
@@ -1107,3 +1108,94 @@ def _combine_schemas(route: HTTPRoute) -> dict[str, Any]:
|
|
|
1107
1108
|
result = compress_schema(result)
|
|
1108
1109
|
|
|
1109
1110
|
return result
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
def extract_output_schema_from_responses(
|
|
1114
|
+
responses: dict[str, ResponseInfo], schema_definitions: dict[str, Any] | None = None
|
|
1115
|
+
) -> dict[str, Any] | None:
|
|
1116
|
+
"""
|
|
1117
|
+
Extract output schema from OpenAPI responses for use as MCP tool output schema.
|
|
1118
|
+
|
|
1119
|
+
This function finds the first successful response (200, 201, 202, 204) with a
|
|
1120
|
+
JSON-compatible content type and extracts its schema. If the schema is not an
|
|
1121
|
+
object type, it wraps it to comply with MCP requirements.
|
|
1122
|
+
|
|
1123
|
+
Args:
|
|
1124
|
+
responses: Dictionary of ResponseInfo objects keyed by status code
|
|
1125
|
+
schema_definitions: Optional schema definitions to include in the output schema
|
|
1126
|
+
|
|
1127
|
+
Returns:
|
|
1128
|
+
dict: MCP-compliant output schema with potential wrapping, or None if no suitable schema found
|
|
1129
|
+
"""
|
|
1130
|
+
if not responses:
|
|
1131
|
+
return None
|
|
1132
|
+
|
|
1133
|
+
# Priority order for success status codes
|
|
1134
|
+
success_codes = ["200", "201", "202", "204"]
|
|
1135
|
+
|
|
1136
|
+
# Find the first successful response
|
|
1137
|
+
response_info = None
|
|
1138
|
+
for status_code in success_codes:
|
|
1139
|
+
if status_code in responses:
|
|
1140
|
+
response_info = responses[status_code]
|
|
1141
|
+
break
|
|
1142
|
+
|
|
1143
|
+
# If no explicit success codes, try any 2xx response
|
|
1144
|
+
if response_info is None:
|
|
1145
|
+
for status_code, resp_info in responses.items():
|
|
1146
|
+
if status_code.startswith("2"):
|
|
1147
|
+
response_info = resp_info
|
|
1148
|
+
break
|
|
1149
|
+
|
|
1150
|
+
if response_info is None or not response_info.content_schema:
|
|
1151
|
+
return None
|
|
1152
|
+
|
|
1153
|
+
# Prefer application/json, then fall back to other JSON-compatible types
|
|
1154
|
+
json_compatible_types = [
|
|
1155
|
+
"application/json",
|
|
1156
|
+
"application/vnd.api+json",
|
|
1157
|
+
"application/hal+json",
|
|
1158
|
+
"application/ld+json",
|
|
1159
|
+
"text/json",
|
|
1160
|
+
]
|
|
1161
|
+
|
|
1162
|
+
schema = None
|
|
1163
|
+
for content_type in json_compatible_types:
|
|
1164
|
+
if content_type in response_info.content_schema:
|
|
1165
|
+
schema = response_info.content_schema[content_type]
|
|
1166
|
+
break
|
|
1167
|
+
|
|
1168
|
+
# If no JSON-compatible type found, try the first available content type
|
|
1169
|
+
if schema is None and response_info.content_schema:
|
|
1170
|
+
first_content_type = next(iter(response_info.content_schema))
|
|
1171
|
+
schema = response_info.content_schema[first_content_type]
|
|
1172
|
+
logger.debug(
|
|
1173
|
+
f"Using non-JSON content type for output schema: {first_content_type}"
|
|
1174
|
+
)
|
|
1175
|
+
|
|
1176
|
+
if not schema or not isinstance(schema, dict):
|
|
1177
|
+
return None
|
|
1178
|
+
|
|
1179
|
+
# Clean and copy the schema
|
|
1180
|
+
output_schema = schema.copy()
|
|
1181
|
+
|
|
1182
|
+
# MCP requires output schemas to be objects. If this schema is not an object,
|
|
1183
|
+
# we need to wrap it similar to how ParsedFunction.from_function() does it
|
|
1184
|
+
if output_schema.get("type") != "object":
|
|
1185
|
+
# Create a wrapped schema that contains the original schema under a "result" key
|
|
1186
|
+
wrapped_schema = {
|
|
1187
|
+
"type": "object",
|
|
1188
|
+
"properties": {"result": output_schema},
|
|
1189
|
+
"required": ["result"],
|
|
1190
|
+
"x-fastmcp-wrap-result": True,
|
|
1191
|
+
}
|
|
1192
|
+
output_schema = wrapped_schema
|
|
1193
|
+
|
|
1194
|
+
# Add schema definitions if available
|
|
1195
|
+
if schema_definitions:
|
|
1196
|
+
output_schema["$defs"] = schema_definitions
|
|
1197
|
+
|
|
1198
|
+
# Use compress_schema to remove unused definitions
|
|
1199
|
+
output_schema = compress_schema(output_schema)
|
|
1200
|
+
|
|
1201
|
+
return output_schema
|
fastmcp/utilities/tests.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
|
+
import logging
|
|
4
5
|
import multiprocessing
|
|
5
6
|
import socket
|
|
6
7
|
import time
|
|
@@ -129,3 +130,15 @@ def run_server_in_process(
|
|
|
129
130
|
proc.join(timeout=2)
|
|
130
131
|
if proc.is_alive():
|
|
131
132
|
raise RuntimeError("Server process failed to terminate even after kill")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@contextmanager
|
|
136
|
+
def caplog_for_fastmcp(caplog):
|
|
137
|
+
"""Context manager to capture logs from FastMCP loggers even when propagation is disabled."""
|
|
138
|
+
caplog.clear()
|
|
139
|
+
logger = logging.getLogger("FastMCP")
|
|
140
|
+
logger.addHandler(caplog.handler)
|
|
141
|
+
try:
|
|
142
|
+
yield
|
|
143
|
+
finally:
|
|
144
|
+
logger.removeHandler(caplog.handler)
|
fastmcp/utilities/types.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import base64
|
|
4
4
|
import inspect
|
|
5
5
|
import mimetypes
|
|
6
|
+
import os
|
|
6
7
|
from collections.abc import Callable
|
|
7
8
|
from functools import lru_cache
|
|
8
9
|
from pathlib import Path
|
|
@@ -101,7 +102,7 @@ class Image:
|
|
|
101
102
|
if path is not None and data is not None:
|
|
102
103
|
raise ValueError("Only one of path or data can be provided")
|
|
103
104
|
|
|
104
|
-
self.path = Path(path) if path else None
|
|
105
|
+
self.path = Path(os.path.expandvars(str(path))).expanduser() if path else None
|
|
105
106
|
self.data = data
|
|
106
107
|
self._format = format
|
|
107
108
|
self._mime_type = self._get_mime_type()
|
|
@@ -160,7 +161,7 @@ class Audio:
|
|
|
160
161
|
if path is not None and data is not None:
|
|
161
162
|
raise ValueError("Only one of path or data can be provided")
|
|
162
163
|
|
|
163
|
-
self.path = Path(path) if path else None
|
|
164
|
+
self.path = Path(os.path.expandvars(str(path))).expanduser() if path else None
|
|
164
165
|
self.data = data
|
|
165
166
|
self._format = format
|
|
166
167
|
self._mime_type = self._get_mime_type()
|
|
@@ -219,7 +220,7 @@ class File:
|
|
|
219
220
|
if path is not None and data is not None:
|
|
220
221
|
raise ValueError("Only one of path or data can be provided")
|
|
221
222
|
|
|
222
|
-
self.path = Path(path) if path else None
|
|
223
|
+
self.path = Path(os.path.expandvars(str(path))).expanduser() if path else None
|
|
223
224
|
self.data = data
|
|
224
225
|
self._format = format
|
|
225
226
|
self._mime_type = self._get_mime_type()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastmcp
|
|
3
|
-
Version: 2.10.
|
|
4
|
-
Summary: The fast, Pythonic way to build MCP servers.
|
|
3
|
+
Version: 2.10.3
|
|
4
|
+
Summary: The fast, Pythonic way to build MCP servers and clients.
|
|
5
5
|
Project-URL: Homepage, https://gofastmcp.com
|
|
6
6
|
Project-URL: Repository, https://github.com/jlowin/fastmcp
|
|
7
7
|
Project-URL: Documentation, https://gofastmcp.com
|
|
@@ -18,14 +18,15 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
18
18
|
Classifier: Typing :: Typed
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
20
|
Requires-Dist: authlib>=1.5.2
|
|
21
|
+
Requires-Dist: cyclopts>=3.0.0
|
|
21
22
|
Requires-Dist: exceptiongroup>=1.2.2
|
|
22
23
|
Requires-Dist: httpx>=0.28.1
|
|
23
24
|
Requires-Dist: mcp>=1.10.0
|
|
24
25
|
Requires-Dist: openapi-pydantic>=0.5.1
|
|
25
26
|
Requires-Dist: pydantic[email]>=2.11.7
|
|
27
|
+
Requires-Dist: pyperclip>=1.9.0
|
|
26
28
|
Requires-Dist: python-dotenv>=1.1.0
|
|
27
29
|
Requires-Dist: rich>=13.9.4
|
|
28
|
-
Requires-Dist: typer>=0.15.2
|
|
29
30
|
Provides-Extra: websockets
|
|
30
31
|
Requires-Dist: websockets>=15.0.1; extra == 'websockets'
|
|
31
32
|
Description-Content-Type: text/markdown
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
fastmcp/__init__.py,sha256=5ChT4kg3srdFl0-9dZekGqpzCESlpc6ohrfPbWf1aTo,1300
|
|
2
2
|
fastmcp/exceptions.py,sha256=-krEavxwddQau6T7MESCR4VjKNLfP9KHJrU1p3y72FU,744
|
|
3
|
+
fastmcp/mcp_config.py,sha256=hOF_NO7F9LbrbmRTj99TVSqHIul4sExNpf1T1HRLpzo,9606
|
|
3
4
|
fastmcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
fastmcp/settings.py,sha256=
|
|
5
|
+
fastmcp/settings.py,sha256=Ta0TKA75xda9sNkIOpPVIEEk4W9jf_2gwcmO26uDQpg,8946
|
|
5
6
|
fastmcp/cli/__init__.py,sha256=Ii284TNoG5lxTP40ETMGhHEq3lQZWxu9m9JuU57kUpQ,87
|
|
6
7
|
fastmcp/cli/claude.py,sha256=IAlcZ4qZKBBj09jZUMEx7EANZE_IR3vcu7zOBJmMOuU,4567
|
|
7
|
-
fastmcp/cli/cli.py,sha256=
|
|
8
|
-
fastmcp/cli/run.py,sha256=
|
|
8
|
+
fastmcp/cli/cli.py,sha256=mr8uKvFINEs4eh-v3vSOmncFCaUS4G0CCk8YVBh_jfk,12679
|
|
9
|
+
fastmcp/cli/run.py,sha256=h2WjJtrmhHQN1kM8CYa5i177hBuHoPsbycSMiOr_8uw,6696
|
|
10
|
+
fastmcp/cli/install/__init__.py,sha256=afaNANIhivfKi4QhJdYUP56Q2XZ4da4dUbDq9HAMJqo,701
|
|
11
|
+
fastmcp/cli/install/claude_code.py,sha256=eH6Pdre-FtzGuEatWYIFVTNtEEjHb7m-bgX4ARIFG2g,5384
|
|
12
|
+
fastmcp/cli/install/claude_desktop.py,sha256=o3p3KiW0QzsrzOEa3OU_gkvzTuFSkabDwEqwwgp1mYU,5590
|
|
13
|
+
fastmcp/cli/install/cursor.py,sha256=296a22T3fdaC8deCwh3TreVFoJ0QpjLxRF1gNsp3Wjg,5555
|
|
14
|
+
fastmcp/cli/install/mcp_config.py,sha256=D1vkZj13ofNFbFhdn8eAuEx-CHTr3nGiVkjSZTdnetY,4559
|
|
15
|
+
fastmcp/cli/install/shared.py,sha256=Y0YZei1YemVCkg0ieUgfRse-lqSlIn5Ho8t6pB9nDa4,2683
|
|
9
16
|
fastmcp/client/__init__.py,sha256=kd2hhSuD8rZuF87c9zlPJP_icJ-Rx3exyNoK0EzfOtE,617
|
|
10
|
-
fastmcp/client/client.py,sha256=
|
|
11
|
-
fastmcp/client/elicitation.py,sha256=
|
|
17
|
+
fastmcp/client/client.py,sha256=GniETS28L8B-ahzztU2ZLI_XSCcEib-miGzE2ZnG4Xc,34054
|
|
18
|
+
fastmcp/client/elicitation.py,sha256=Jf9yqna8R7r1hqedXAyh9a2-QNVzbCSKUDZhkFHqHqg,2403
|
|
12
19
|
fastmcp/client/logging.py,sha256=7GJ-BLFW16_IOJPlGTNEWPP0P-yqqRpmsLdiKrlVsw8,757
|
|
13
20
|
fastmcp/client/messages.py,sha256=NIPjt-5js_DkI5BD4OVdTf6pz-nGjc2dtbgt-vAY234,4329
|
|
14
21
|
fastmcp/client/oauth_callback.py,sha256=ODAnVX-ettL82RuI5KpfkKf8iDtYMDue3Tnab5sjQtM,10071
|
|
15
22
|
fastmcp/client/progress.py,sha256=WjLLDbUKMsx8DK-fqO7AGsXb83ak-6BMrLvzzznGmcI,1043
|
|
16
23
|
fastmcp/client/roots.py,sha256=IxI_bHwHTmg6c2H-s1av1ZgrRnNDieHtYwdGFbzXT5c,2471
|
|
17
24
|
fastmcp/client/sampling.py,sha256=Q8PzYCERa1W3xGGI9I9QOhhDM-M4i3P5lESb0cp2iI8,1595
|
|
18
|
-
fastmcp/client/transports.py,sha256=
|
|
25
|
+
fastmcp/client/transports.py,sha256=AIQ0ni3lI0MoewgPNlkLHvym1iEnc8vCa513lHKCIqA,33803
|
|
19
26
|
fastmcp/client/auth/__init__.py,sha256=4DNsfp4iaQeBcpds0JDdMn6Mmfud44stWLsret0sVKY,91
|
|
20
27
|
fastmcp/client/auth/bearer.py,sha256=MFEFqcH6u_V86msYiOsEFKN5ks1V9BnBNiPsPLHUTqo,399
|
|
21
28
|
fastmcp/client/auth/oauth.py,sha256=pSyuI0FlRK1qkBA6mvq-bxKzl2B-pMCvPPzIByTJBEo,11745
|
|
@@ -34,12 +41,12 @@ fastmcp/contrib/mcp_mixin/__init__.py,sha256=aw9IQ1ssNjCgws4ZNt8bkdpossAAGVAwwjB
|
|
|
34
41
|
fastmcp/contrib/mcp_mixin/example.py,sha256=GnunkXmtG5hLLTUsM8aW5ZURU52Z8vI4tNLl-fK7Dg0,1228
|
|
35
42
|
fastmcp/contrib/mcp_mixin/mcp_mixin.py,sha256=sUSJ2o0sTsb061MyPN2xuYP0oI4W6YVQXupY3nnjD50,8687
|
|
36
43
|
fastmcp/prompts/__init__.py,sha256=An8uMBUh9Hrb7qqcn_5_Hent7IOeSh7EA2IUVsIrtHc,179
|
|
37
|
-
fastmcp/prompts/prompt.py,sha256=
|
|
44
|
+
fastmcp/prompts/prompt.py,sha256=Ux_FT8GTnejvjDWEgf5nSAIwbhV-9otgCJm-EqnzxvY,13861
|
|
38
45
|
fastmcp/prompts/prompt_manager.py,sha256=Pt9cg_c9FR2EA0ITIHJT5Utihkd383JzhqhT-y2VnKo,7677
|
|
39
46
|
fastmcp/resources/__init__.py,sha256=y1iAuqx-GIrS1NqIYzKezIDiYyjNEzzHD35epHpMnXE,463
|
|
40
|
-
fastmcp/resources/resource.py,sha256=
|
|
47
|
+
fastmcp/resources/resource.py,sha256=GGCUHUQe4ALrVJc-Ng1uz8-mtQSg_vuVA71H_qJkko0,6031
|
|
41
48
|
fastmcp/resources/resource_manager.py,sha256=zjhso9ZP0EK_beTpUz_amqJ7XSABqU8mqPSk-JturOE,19777
|
|
42
|
-
fastmcp/resources/template.py,sha256=
|
|
49
|
+
fastmcp/resources/template.py,sha256=mYsw3xPWRq1AgOVFkG_A1otY3NXYw5oUJnVzMC1Qy80,10346
|
|
43
50
|
fastmcp/resources/types.py,sha256=SiYNLnpXT-mHgNUrzqKUvXYUsY-V3gwJIrYdJfFwDDo,4868
|
|
44
51
|
fastmcp/server/__init__.py,sha256=bMD4aQD4yJqLz7-mudoNsyeV8UgQfRAg3PRwPvwTEds,119
|
|
45
52
|
fastmcp/server/context.py,sha256=OFkdgT53NkN_VYwwHjdR2C_28CWtPsFln36X0QLM23g,20258
|
|
@@ -47,13 +54,13 @@ fastmcp/server/dependencies.py,sha256=iKJdz1XsVJcrfHo_reXj9ZSldw-HeAwsp9S6lAgfGA
|
|
|
47
54
|
fastmcp/server/elicitation.py,sha256=jZIHjV4NjhYbT-w8pBArwd0vNzP8OYwzmsnWDdk6Bd0,6136
|
|
48
55
|
fastmcp/server/http.py,sha256=d0Jij4HVTaAohluRXArSniXLb1HcHP3ytbe-mMHg6nE,11678
|
|
49
56
|
fastmcp/server/low_level.py,sha256=LNmc_nU_wx-fRG8OEHdLPKopZpovcrWlyAxJzKss3TA,1239
|
|
50
|
-
fastmcp/server/openapi.py,sha256=
|
|
51
|
-
fastmcp/server/proxy.py,sha256=
|
|
52
|
-
fastmcp/server/server.py,sha256=
|
|
57
|
+
fastmcp/server/openapi.py,sha256=YoARNzEZQs6MfnGfl5-V9kULCDp1LluJL2yeH9m62Os,37595
|
|
58
|
+
fastmcp/server/proxy.py,sha256=YxlU4lNPGb5rk4oyUySYiOMz3GIAywrQP1rMcVYSih4,21689
|
|
59
|
+
fastmcp/server/server.py,sha256=Zk77BcHyr0lSwf2LmNOQXiDJNFeQKkZ_p20dg81P0IU,82383
|
|
53
60
|
fastmcp/server/auth/__init__.py,sha256=doHCLwOIElvH1NrTdpeP9JKfnNf3MDYPSpQfdsQ-uI0,84
|
|
54
61
|
fastmcp/server/auth/auth.py,sha256=A00OKxglEMrGMMIiMbc6UmpGc2VoWDkEVU5g2pIzDIg,2119
|
|
55
62
|
fastmcp/server/auth/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
-
fastmcp/server/auth/providers/bearer.py,sha256=
|
|
63
|
+
fastmcp/server/auth/providers/bearer.py,sha256=rjwuKFgCm4fLsAQzUaIaE8OD6bml3sBaO7NYq3Wgs24,16718
|
|
57
64
|
fastmcp/server/auth/providers/bearer_env.py,sha256=NYPCW363Q8u8BdiPPz1FdB3_kwmbCaWT5yKdAO-ZgwA,2081
|
|
58
65
|
fastmcp/server/auth/providers/in_memory.py,sha256=Sb3GOtLL2bWbm8z-T8cEsMz1qcQUSHpPEEgYRvTOQi4,14251
|
|
59
66
|
fastmcp/server/middleware/__init__.py,sha256=m1QJFQ7JW_2JHpJp1FurBNYaxbBUa_HyDn1Bw9mtyvc,367
|
|
@@ -63,24 +70,24 @@ fastmcp/server/middleware/middleware.py,sha256=1Zy5KdBCrGzqFIpYZqWlZq9rLK3NDlRiX
|
|
|
63
70
|
fastmcp/server/middleware/rate_limiting.py,sha256=VTrCoQFmWCm0BxwOrNfG21CBFDDOKJT7IiSEjpJgmPA,7921
|
|
64
71
|
fastmcp/server/middleware/timing.py,sha256=lL_xc-ErLD5lplfvd5-HIyWEbZhgNBYkcQ74KFXAMkA,5591
|
|
65
72
|
fastmcp/tools/__init__.py,sha256=vzqb-Y7Kf0d5T0aOsld-O-FA8kD7-4uFExChewFHEzY,201
|
|
66
|
-
fastmcp/tools/tool.py,sha256=
|
|
73
|
+
fastmcp/tools/tool.py,sha256=fW-xZQCLN2qSZrlAo2eQ1aEFa-VN5jYEHx5H936-_50,17466
|
|
67
74
|
fastmcp/tools/tool_manager.py,sha256=Sm_tOO-SY0m7tEN_dofP-tvBnC2HroPRKLU6sp8gnUw,7739
|
|
68
|
-
fastmcp/tools/tool_transform.py,sha256=
|
|
75
|
+
fastmcp/tools/tool_transform.py,sha256=nGvxxKsyfp3gYl_nkYFHlnb8Fc4Jtw6t7WL291S4Vh4,32558
|
|
69
76
|
fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
|
|
70
77
|
fastmcp/utilities/cache.py,sha256=aV3oZ-ZhMgLSM9iAotlUlEy5jFvGXrVo0Y5Bj4PBtqY,707
|
|
78
|
+
fastmcp/utilities/cli.py,sha256=n3HA8IXg7hKaizuM3SRCW65UMXlqjUDf5USRSuscTdY,3287
|
|
71
79
|
fastmcp/utilities/components.py,sha256=WIxNVZ7YxCLpdIm_pbTYeP0lAxikvgptVYhIL0LVmCc,2535
|
|
72
80
|
fastmcp/utilities/exceptions.py,sha256=7Z9j5IzM5rT27BC1Mcn8tkS-bjqCYqMKwb2MMTaxJYU,1350
|
|
73
81
|
fastmcp/utilities/http.py,sha256=1ns1ymBS-WSxbZjGP6JYjSO52Wa_ls4j4WbnXiupoa4,245
|
|
74
82
|
fastmcp/utilities/inspect.py,sha256=XNA0dfYM5G-FVbJaVJO8loSUUCNypyLA-QjqTOneJyU,10833
|
|
75
|
-
fastmcp/utilities/json_schema.py,sha256=
|
|
83
|
+
fastmcp/utilities/json_schema.py,sha256=8qyb3qOvk1gc3p63uP6LGyKdOANkNdD9YA32OiBxyNw,5495
|
|
76
84
|
fastmcp/utilities/json_schema_type.py,sha256=Sml03nJGOnUfxCGrHWRMwZMultV0X5JThMepUnHIUiA,22377
|
|
77
|
-
fastmcp/utilities/logging.py,sha256=
|
|
78
|
-
fastmcp/utilities/
|
|
79
|
-
fastmcp/utilities/
|
|
80
|
-
fastmcp/utilities/
|
|
81
|
-
fastmcp/
|
|
82
|
-
fastmcp-2.10.
|
|
83
|
-
fastmcp-2.10.
|
|
84
|
-
fastmcp-2.10.
|
|
85
|
-
fastmcp-2.10.
|
|
86
|
-
fastmcp-2.10.1.dist-info/RECORD,,
|
|
85
|
+
fastmcp/utilities/logging.py,sha256=1y7oNmy8WrR0NsfNVw1LPoKu92OFdmzIO65syOKi_BI,1388
|
|
86
|
+
fastmcp/utilities/openapi.py,sha256=YWDFjT9ng4xi_kzNCoeBCgwnlep14IQ7PnCSQt_lVyU,48528
|
|
87
|
+
fastmcp/utilities/tests.py,sha256=kZH8HQAC702a5vNJb4K0tO1ll9CZADWQ_P-5ERWSvSA,4242
|
|
88
|
+
fastmcp/utilities/types.py,sha256=c6HPvHCpkq8EXh0hWjaUlj9aCZklmxzAQHCXZy7llNo,10636
|
|
89
|
+
fastmcp-2.10.3.dist-info/METADATA,sha256=ZQWYX4dWpwwDXmCBG4EiiXACSF8Xv6qag6WKuqcnY2s,17842
|
|
90
|
+
fastmcp-2.10.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
91
|
+
fastmcp-2.10.3.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
|
|
92
|
+
fastmcp-2.10.3.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
93
|
+
fastmcp-2.10.3.dist-info/RECORD,,
|
fastmcp/utilities/mcp_config.py
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
|
5
|
-
from urllib.parse import urlparse
|
|
6
|
-
|
|
7
|
-
import httpx
|
|
8
|
-
from pydantic import AnyUrl, ConfigDict, Field
|
|
9
|
-
|
|
10
|
-
from fastmcp.utilities.types import FastMCPBaseModel
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from fastmcp.client.transports import (
|
|
14
|
-
SSETransport,
|
|
15
|
-
StdioTransport,
|
|
16
|
-
StreamableHttpTransport,
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def infer_transport_type_from_url(
|
|
21
|
-
url: str | AnyUrl,
|
|
22
|
-
) -> Literal["http", "sse"]:
|
|
23
|
-
"""
|
|
24
|
-
Infer the appropriate transport type from the given URL.
|
|
25
|
-
"""
|
|
26
|
-
url = str(url)
|
|
27
|
-
if not url.startswith("http"):
|
|
28
|
-
raise ValueError(f"Invalid URL: {url}")
|
|
29
|
-
|
|
30
|
-
parsed_url = urlparse(url)
|
|
31
|
-
path = parsed_url.path
|
|
32
|
-
|
|
33
|
-
# Match /sse followed by /, ?, &, or end of string
|
|
34
|
-
if re.search(r"/sse(/|\?|&|$)", path):
|
|
35
|
-
return "sse"
|
|
36
|
-
else:
|
|
37
|
-
return "http"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class StdioMCPServer(FastMCPBaseModel):
|
|
41
|
-
command: str
|
|
42
|
-
args: list[str] = Field(default_factory=list)
|
|
43
|
-
env: dict[str, Any] = Field(default_factory=dict)
|
|
44
|
-
cwd: str | None = None
|
|
45
|
-
transport: Literal["stdio"] = "stdio"
|
|
46
|
-
|
|
47
|
-
def to_transport(self) -> StdioTransport:
|
|
48
|
-
from fastmcp.client.transports import StdioTransport
|
|
49
|
-
|
|
50
|
-
return StdioTransport(
|
|
51
|
-
command=self.command,
|
|
52
|
-
args=self.args,
|
|
53
|
-
env=self.env,
|
|
54
|
-
cwd=self.cwd,
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class RemoteMCPServer(FastMCPBaseModel):
|
|
59
|
-
url: str
|
|
60
|
-
headers: dict[str, str] = Field(default_factory=dict)
|
|
61
|
-
transport: Literal["http", "streamable-http", "sse"] | None = None
|
|
62
|
-
auth: Annotated[
|
|
63
|
-
str | Literal["oauth"] | httpx.Auth | None,
|
|
64
|
-
Field(
|
|
65
|
-
description='Either a string representing a Bearer token, the literal "oauth" to use OAuth authentication, or an httpx.Auth instance for custom authentication.',
|
|
66
|
-
),
|
|
67
|
-
] = None
|
|
68
|
-
|
|
69
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
70
|
-
|
|
71
|
-
def to_transport(self) -> StreamableHttpTransport | SSETransport:
|
|
72
|
-
from fastmcp.client.transports import SSETransport, StreamableHttpTransport
|
|
73
|
-
|
|
74
|
-
if self.transport is None:
|
|
75
|
-
transport = infer_transport_type_from_url(self.url)
|
|
76
|
-
else:
|
|
77
|
-
transport = self.transport
|
|
78
|
-
|
|
79
|
-
if transport == "sse":
|
|
80
|
-
return SSETransport(self.url, headers=self.headers, auth=self.auth)
|
|
81
|
-
else:
|
|
82
|
-
# Both "http" and "streamable-http" map to StreamableHttpTransport
|
|
83
|
-
return StreamableHttpTransport(
|
|
84
|
-
self.url, headers=self.headers, auth=self.auth
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
class MCPConfig(FastMCPBaseModel):
|
|
89
|
-
mcpServers: dict[str, StdioMCPServer | RemoteMCPServer]
|
|
90
|
-
|
|
91
|
-
@classmethod
|
|
92
|
-
def from_dict(cls, config: dict[str, Any]) -> MCPConfig:
|
|
93
|
-
return cls(mcpServers=config.get("mcpServers", config))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|