fastmcp 2.5.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/transports.py +5 -34
- fastmcp/server/dependencies.py +32 -0
- fastmcp/server/openapi.py +3 -22
- {fastmcp-2.5.0.dist-info → fastmcp-2.5.1.dist-info}/METADATA +1 -1
- {fastmcp-2.5.0.dist-info → fastmcp-2.5.1.dist-info}/RECORD +8 -8
- {fastmcp-2.5.0.dist-info → fastmcp-2.5.1.dist-info}/WHEEL +0 -0
- {fastmcp-2.5.0.dist-info → fastmcp-2.5.1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.5.0.dist-info → fastmcp-2.5.1.dist-info}/licenses/LICENSE +0 -0
fastmcp/client/transports.py
CHANGED
|
@@ -25,7 +25,7 @@ from pydantic import AnyUrl
|
|
|
25
25
|
from typing_extensions import Unpack
|
|
26
26
|
|
|
27
27
|
from fastmcp.server import FastMCP as FastMCPServer
|
|
28
|
-
from fastmcp.server.dependencies import
|
|
28
|
+
from fastmcp.server.dependencies import get_http_headers
|
|
29
29
|
from fastmcp.server.server import FastMCP
|
|
30
30
|
from fastmcp.utilities.logging import get_logger
|
|
31
31
|
from fastmcp.utilities.mcp_config import MCPConfig, infer_transport_type_from_url
|
|
@@ -35,11 +35,6 @@ if TYPE_CHECKING:
|
|
|
35
35
|
|
|
36
36
|
logger = get_logger(__name__)
|
|
37
37
|
|
|
38
|
-
# these headers, when forwarded to the remote server, can cause issues
|
|
39
|
-
EXCLUDE_HEADERS = {
|
|
40
|
-
"content-length",
|
|
41
|
-
}
|
|
42
|
-
|
|
43
38
|
|
|
44
39
|
class SessionKwargs(TypedDict, total=False):
|
|
45
40
|
"""Keyword arguments for the MCP ClientSession constructor."""
|
|
@@ -138,23 +133,12 @@ class SSETransport(ClientTransport):
|
|
|
138
133
|
async def connect_session(
|
|
139
134
|
self, **session_kwargs: Unpack[SessionKwargs]
|
|
140
135
|
) -> AsyncIterator[ClientSession]:
|
|
141
|
-
client_kwargs: dict[str, Any] = {
|
|
142
|
-
"headers": self.headers,
|
|
143
|
-
}
|
|
136
|
+
client_kwargs: dict[str, Any] = {}
|
|
144
137
|
|
|
145
138
|
# load headers from an active HTTP request, if available. This will only be true
|
|
146
139
|
# if the client is used in a FastMCP Proxy, in which case the MCP client headers
|
|
147
140
|
# need to be forwarded to the remote server.
|
|
148
|
-
|
|
149
|
-
active_request = get_http_request()
|
|
150
|
-
for name, value in active_request.headers.items():
|
|
151
|
-
name = name.lower()
|
|
152
|
-
if name not in self.headers and name not in {
|
|
153
|
-
h.lower() for h in EXCLUDE_HEADERS
|
|
154
|
-
}:
|
|
155
|
-
client_kwargs["headers"][name] = str(value)
|
|
156
|
-
except RuntimeError:
|
|
157
|
-
client_kwargs["headers"] = self.headers
|
|
141
|
+
client_kwargs["headers"] = get_http_headers() | self.headers
|
|
158
142
|
|
|
159
143
|
# sse_read_timeout has a default value set, so we can't pass None without overriding it
|
|
160
144
|
# instead we simply leave the kwarg out if it's not provided
|
|
@@ -201,25 +185,12 @@ class StreamableHttpTransport(ClientTransport):
|
|
|
201
185
|
async def connect_session(
|
|
202
186
|
self, **session_kwargs: Unpack[SessionKwargs]
|
|
203
187
|
) -> AsyncIterator[ClientSession]:
|
|
204
|
-
client_kwargs: dict[str, Any] = {
|
|
205
|
-
"headers": self.headers,
|
|
206
|
-
}
|
|
188
|
+
client_kwargs: dict[str, Any] = {}
|
|
207
189
|
|
|
208
190
|
# load headers from an active HTTP request, if available. This will only be true
|
|
209
191
|
# if the client is used in a FastMCP Proxy, in which case the MCP client headers
|
|
210
192
|
# need to be forwarded to the remote server.
|
|
211
|
-
|
|
212
|
-
active_request = get_http_request()
|
|
213
|
-
for name, value in active_request.headers.items():
|
|
214
|
-
name = name.lower()
|
|
215
|
-
if name not in self.headers and name not in {
|
|
216
|
-
h.lower() for h in EXCLUDE_HEADERS
|
|
217
|
-
}:
|
|
218
|
-
client_kwargs["headers"][name] = str(value)
|
|
219
|
-
|
|
220
|
-
except RuntimeError:
|
|
221
|
-
client_kwargs["headers"] = self.headers
|
|
222
|
-
print(client_kwargs)
|
|
193
|
+
client_kwargs["headers"] = get_http_headers() | self.headers
|
|
223
194
|
|
|
224
195
|
# sse_read_timeout has a default value set, so we can't pass None without overriding it
|
|
225
196
|
# instead we simply leave the kwarg out if it's not provided
|
fastmcp/server/dependencies.py
CHANGED
|
@@ -33,3 +33,35 @@ def get_http_request() -> Request:
|
|
|
33
33
|
if request is None:
|
|
34
34
|
raise RuntimeError("No active HTTP request found.")
|
|
35
35
|
return request
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_http_headers(include_all: bool = False) -> dict[str, str]:
|
|
39
|
+
"""
|
|
40
|
+
Extract headers from the current HTTP request if available.
|
|
41
|
+
|
|
42
|
+
Never raises an exception, even if there is no active HTTP request (in which case
|
|
43
|
+
an empty dict is returned).
|
|
44
|
+
|
|
45
|
+
By default, strips problematic headers like `content-length` that cause issues if forwarded to downstream clients.
|
|
46
|
+
If `include_all` is True, all headers are returned.
|
|
47
|
+
"""
|
|
48
|
+
if include_all:
|
|
49
|
+
exclude_headers = set()
|
|
50
|
+
else:
|
|
51
|
+
exclude_headers = {"content-length"}
|
|
52
|
+
|
|
53
|
+
# ensure all lowercase!
|
|
54
|
+
# (just in case)
|
|
55
|
+
exclude_headers = {h.lower() for h in exclude_headers}
|
|
56
|
+
|
|
57
|
+
headers = {}
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
request = get_http_request()
|
|
61
|
+
for name, value in request.headers.items():
|
|
62
|
+
lower_name = name.lower()
|
|
63
|
+
if lower_name not in exclude_headers:
|
|
64
|
+
headers[lower_name] = str(value)
|
|
65
|
+
return headers
|
|
66
|
+
except RuntimeError:
|
|
67
|
+
return {}
|
fastmcp/server/openapi.py
CHANGED
|
@@ -18,7 +18,7 @@ from pydantic.networks import AnyUrl
|
|
|
18
18
|
|
|
19
19
|
from fastmcp.exceptions import ToolError
|
|
20
20
|
from fastmcp.resources import Resource, ResourceTemplate
|
|
21
|
-
from fastmcp.server.dependencies import
|
|
21
|
+
from fastmcp.server.dependencies import get_http_headers
|
|
22
22
|
from fastmcp.server.server import FastMCP
|
|
23
23
|
from fastmcp.tools.tool import Tool, _convert_to_content
|
|
24
24
|
from fastmcp.utilities import openapi
|
|
@@ -60,25 +60,6 @@ def _slugify(text: str) -> str:
|
|
|
60
60
|
return slug
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def _get_mcp_client_headers() -> dict[str, str]:
|
|
64
|
-
"""
|
|
65
|
-
Extract headers from the current MCP client HTTP request if available.
|
|
66
|
-
|
|
67
|
-
These headers will take precedence over OpenAPI-defined headers when both are present.
|
|
68
|
-
|
|
69
|
-
Returns:
|
|
70
|
-
Dictionary of header name-value pairs (lowercased names), or empty dict if no HTTP request is active.
|
|
71
|
-
"""
|
|
72
|
-
try:
|
|
73
|
-
http_request = get_http_request()
|
|
74
|
-
return {
|
|
75
|
-
name.lower(): str(value) for name, value in http_request.headers.items()
|
|
76
|
-
}
|
|
77
|
-
except RuntimeError:
|
|
78
|
-
# No active HTTP request (e.g., STDIO transport), return empty dict
|
|
79
|
-
return {}
|
|
80
|
-
|
|
81
|
-
|
|
82
63
|
# Type definitions for the mapping functions
|
|
83
64
|
RouteMapFn = Callable[[HTTPRoute, "MCPType"], "MCPType | None"]
|
|
84
65
|
ComponentFn = Callable[
|
|
@@ -423,7 +404,7 @@ class OpenAPITool(Tool):
|
|
|
423
404
|
headers.update(openapi_headers)
|
|
424
405
|
|
|
425
406
|
# Add headers from the current MCP client HTTP request (these take precedence)
|
|
426
|
-
mcp_headers =
|
|
407
|
+
mcp_headers = get_http_headers()
|
|
427
408
|
headers.update(mcp_headers)
|
|
428
409
|
|
|
429
410
|
# Prepare request body
|
|
@@ -574,7 +555,7 @@ class OpenAPIResource(Resource):
|
|
|
574
555
|
|
|
575
556
|
# Prepare headers from MCP client request if available
|
|
576
557
|
headers = {}
|
|
577
|
-
mcp_headers =
|
|
558
|
+
mcp_headers = get_http_headers()
|
|
578
559
|
headers.update(mcp_headers)
|
|
579
560
|
|
|
580
561
|
response = await self._client.request(
|
|
@@ -13,7 +13,7 @@ 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
|
|
@@ -35,9 +35,9 @@ fastmcp/resources/template.py,sha256=mdejT0ofACYrn32Jw3wdJ7bJcVbW_4VMQEwMZLDR3zM
|
|
|
35
35
|
fastmcp/resources/types.py,sha256=5fUFvzRlekNjtfihtq8S-fT0alKoNfclzrugqeM5JRE,6366
|
|
36
36
|
fastmcp/server/__init__.py,sha256=bMD4aQD4yJqLz7-mudoNsyeV8UgQfRAg3PRwPvwTEds,119
|
|
37
37
|
fastmcp/server/context.py,sha256=yN1e0LsnCl7cEpr9WlbvFhSf8oE56kKb-20m8h2SsBY,10171
|
|
38
|
-
fastmcp/server/dependencies.py,sha256=
|
|
38
|
+
fastmcp/server/dependencies.py,sha256=A1A2dKAyZ2GcAA2RQ6KA5SaHuLU3LSbZaGkzncgcX2E,1722
|
|
39
39
|
fastmcp/server/http.py,sha256=wZWUrLvKITlvkxQoggJ9RyvynCUMEJqqMMsvX7Hmb9o,12807
|
|
40
|
-
fastmcp/server/openapi.py,sha256=
|
|
40
|
+
fastmcp/server/openapi.py,sha256=9qXSuEl671sT1F7nSM3SiD5KANGqHUhiL1BBdCnuCcU,39153
|
|
41
41
|
fastmcp/server/proxy.py,sha256=mt3eM6TQWfnZD5XehmTXisskZ4CBbsWyjRPjprlTjBY,9653
|
|
42
42
|
fastmcp/server/server.py,sha256=C0VEnHuSoYt-qef1jm4REPJkcM2stQ2Zp_rT_sELr2Y,57141
|
|
43
43
|
fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
|
|
@@ -53,8 +53,8 @@ fastmcp/utilities/mcp_config.py,sha256=_wY3peaFDEgyOBkJ_Tb8sETk3mtdwtw1053q7ry0z
|
|
|
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.5.
|
|
57
|
-
fastmcp-2.5.
|
|
58
|
-
fastmcp-2.5.
|
|
59
|
-
fastmcp-2.5.
|
|
60
|
-
fastmcp-2.5.
|
|
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
|