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.
@@ -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 get_http_request
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
- try:
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
- try:
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
@@ -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 get_http_request
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 = _get_mcp_client_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 = _get_mcp_client_headers()
558
+ mcp_headers = get_http_headers()
578
559
  headers.update(mcp_headers)
579
560
 
580
561
  response = await self._client.request(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.5.0
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
@@ -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=Bm6KeQffkmfbmRb6BKopxvKOpRPHqcSgt21NTjRpbFE,25358
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=1utkxFsV37HZcWBwI69JyngVN2ppGO_PEgxUlUHHy_Q,742
38
+ fastmcp/server/dependencies.py,sha256=A1A2dKAyZ2GcAA2RQ6KA5SaHuLU3LSbZaGkzncgcX2E,1722
39
39
  fastmcp/server/http.py,sha256=wZWUrLvKITlvkxQoggJ9RyvynCUMEJqqMMsvX7Hmb9o,12807
40
- fastmcp/server/openapi.py,sha256=zzFfVFlGtII_I3vz91SX-NCj6SIl4It8Q1ZlJ_hm7ZA,39807
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.0.dist-info/METADATA,sha256=chPczyVwGPRVD83dZAlmL595OREQc_EsyrBe3rOz17Q,16510
57
- fastmcp-2.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
58
- fastmcp-2.5.0.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
59
- fastmcp-2.5.0.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
60
- fastmcp-2.5.0.dist-info/RECORD,,
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,,