fastmcp 2.3.0__py3-none-any.whl → 2.3.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.
@@ -0,0 +1 @@
1
+ Patched low-level objects. When possible, we prefer the official SDK, but we patch bugs here if necessary.
File without changes
@@ -0,0 +1,104 @@
1
+ import logging
2
+ from contextlib import asynccontextmanager
3
+ from typing import Any
4
+ from urllib.parse import quote
5
+ from uuid import uuid4
6
+
7
+ import anyio
8
+ from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
9
+ from mcp.server.sse import SseServerTransport as LowLevelSSEServerTransport
10
+ from mcp.shared.message import SessionMessage
11
+ from sse_starlette import EventSourceResponse
12
+ from starlette.types import Receive, Scope, Send
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class SseServerTransport(LowLevelSSEServerTransport):
18
+ """
19
+ Patched SSE server transport
20
+ """
21
+
22
+ @asynccontextmanager
23
+ async def connect_sse(self, scope: Scope, receive: Receive, send: Send):
24
+ """
25
+ See https://github.com/modelcontextprotocol/python-sdk/pull/659/
26
+ """
27
+ if scope["type"] != "http":
28
+ logger.error("connect_sse received non-HTTP request")
29
+ raise ValueError("connect_sse can only handle HTTP requests")
30
+
31
+ logger.debug("Setting up SSE connection")
32
+ read_stream: MemoryObjectReceiveStream[SessionMessage | Exception]
33
+ read_stream_writer: MemoryObjectSendStream[SessionMessage | Exception]
34
+
35
+ write_stream: MemoryObjectSendStream[SessionMessage]
36
+ write_stream_reader: MemoryObjectReceiveStream[SessionMessage]
37
+
38
+ read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
39
+ write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
40
+
41
+ session_id = uuid4()
42
+ self._read_stream_writers[session_id] = read_stream_writer
43
+ logger.debug(f"Created new session with ID: {session_id}")
44
+
45
+ # Determine the full path for the message endpoint to be sent to the client.
46
+ # scope['root_path'] is the prefix where the current Starlette app
47
+ # instance is mounted.
48
+ # e.g., "" if top-level, or "/api_prefix" if mounted under "/api_prefix".
49
+ root_path = scope.get("root_path", "")
50
+
51
+ # self._endpoint is the path *within* this app, e.g., "/messages".
52
+ # Concatenating them gives the full absolute path from the server root.
53
+ # e.g., "" + "/messages" -> "/messages"
54
+ # e.g., "/api_prefix" + "/messages" -> "/api_prefix/messages"
55
+ full_message_path_for_client = root_path.rstrip("/") + self._endpoint
56
+
57
+ # This is the URI (path + query) the client will use to POST messages.
58
+ client_post_uri_data = (
59
+ f"{quote(full_message_path_for_client)}?session_id={session_id.hex}"
60
+ )
61
+
62
+ sse_stream_writer, sse_stream_reader = anyio.create_memory_object_stream[
63
+ dict[str, Any]
64
+ ](0)
65
+
66
+ async def sse_writer():
67
+ logger.debug("Starting SSE writer")
68
+ async with sse_stream_writer, write_stream_reader:
69
+ await sse_stream_writer.send(
70
+ {"event": "endpoint", "data": client_post_uri_data}
71
+ )
72
+ logger.debug(f"Sent endpoint event: {client_post_uri_data}")
73
+
74
+ async for session_message in write_stream_reader:
75
+ logger.debug(f"Sending message via SSE: {session_message}")
76
+ await sse_stream_writer.send(
77
+ {
78
+ "event": "message",
79
+ "data": session_message.message.model_dump_json(
80
+ by_alias=True, exclude_none=True
81
+ ),
82
+ }
83
+ )
84
+
85
+ async with anyio.create_task_group() as tg:
86
+
87
+ async def response_wrapper(scope: Scope, receive: Receive, send: Send):
88
+ """
89
+ The EventSourceResponse returning signals a client close / disconnect.
90
+ In this case we close our side of the streams to signal the client that
91
+ the connection has been closed.
92
+ """
93
+ await EventSourceResponse(
94
+ content=sse_stream_reader, data_sender_callable=sse_writer
95
+ )(scope, receive, send)
96
+ await read_stream_writer.aclose()
97
+ await write_stream_reader.aclose()
98
+ logging.debug(f"Client session disconnected {session_id}")
99
+
100
+ logger.debug("Starting SSE response task")
101
+ tg.start_soon(response_wrapper, scope, receive, send)
102
+
103
+ logger.debug("Yielding read and write streams")
104
+ yield (read_stream, write_stream)
fastmcp/server/http.py CHANGED
@@ -13,7 +13,6 @@ from mcp.server.auth.middleware.bearer_auth import (
13
13
  from mcp.server.auth.provider import OAuthAuthorizationServerProvider
14
14
  from mcp.server.auth.routes import create_auth_routes
15
15
  from mcp.server.auth.settings import AuthSettings
16
- from mcp.server.sse import SseServerTransport
17
16
  from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
18
17
  from starlette.applications import Starlette
19
18
  from starlette.middleware import Middleware
@@ -23,6 +22,7 @@ from starlette.responses import Response
23
22
  from starlette.routing import Mount, Route
24
23
  from starlette.types import Receive, Scope, Send
25
24
 
25
+ from fastmcp.low_level.sse_server_transport import SseServerTransport
26
26
  from fastmcp.utilities.logging import get_logger
27
27
 
28
28
  if TYPE_CHECKING:
@@ -110,7 +110,7 @@ def setup_auth_middleware_and_routes(
110
110
  def create_base_app(
111
111
  routes: list[Route | Mount],
112
112
  middleware: list[Middleware],
113
- debug: bool,
113
+ debug: bool = False,
114
114
  lifespan: Callable | None = None,
115
115
  ) -> Starlette:
116
116
  """Create a base Starlette app with common middleware and routes.
@@ -127,17 +127,12 @@ def create_base_app(
127
127
  # Always add RequestContextMiddleware as the outermost middleware
128
128
  middleware.append(Middleware(RequestContextMiddleware))
129
129
 
130
- # Create the app
131
- app_kwargs = {
132
- "debug": debug,
133
- "routes": routes,
134
- "middleware": middleware,
135
- }
136
-
137
- if lifespan:
138
- app_kwargs["lifespan"] = lifespan
139
-
140
- return Starlette(**app_kwargs)
130
+ return Starlette(
131
+ routes=routes,
132
+ middleware=middleware,
133
+ debug=debug,
134
+ lifespan=lifespan,
135
+ )
141
136
 
142
137
 
143
138
  def create_sse_app(
@@ -224,7 +219,11 @@ def create_sse_app(
224
219
  routes.extend(cast(list[Route | Mount], additional_routes))
225
220
 
226
221
  # Create and return the app
227
- return create_base_app(routes, middleware, debug)
222
+ return create_base_app(
223
+ routes=routes,
224
+ middleware=middleware,
225
+ debug=debug,
226
+ )
228
227
 
229
228
 
230
229
  def create_streamable_http_app(
@@ -305,4 +304,9 @@ def create_streamable_http_app(
305
304
  yield
306
305
 
307
306
  # Create and return the app with lifespan
308
- return create_base_app(routes, middleware, debug, lifespan)
307
+ return create_base_app(
308
+ routes=routes,
309
+ middleware=middleware,
310
+ debug=debug,
311
+ lifespan=lifespan,
312
+ )
fastmcp/server/proxy.py CHANGED
@@ -124,7 +124,7 @@ class ProxyTemplate(ResourceTemplate):
124
124
  params: dict[str, Any],
125
125
  context: Context | None = None,
126
126
  ) -> ProxyResource:
127
- # dont use the provided uri, because it may not be the same as the
127
+ # don't use the provided uri, because it may not be the same as the
128
128
  # uri_template on the remote server.
129
129
  # quote params to ensure they are valid for the uri_template
130
130
  parameterized_uri = self.uri_template.format(
fastmcp/server/server.py CHANGED
@@ -892,7 +892,7 @@ class FastMCP(Generic[LifespanResultT]):
892
892
  future changes to the imported server will not be reflected in the
893
893
  importing server. Server-level configurations and lifespans are not imported.
894
894
 
895
- When an server is mounted: - The tools are imported with prefixed names
895
+ When a server is mounted: - The tools are imported with prefixed names
896
896
  using the tool_separator
897
897
  Example: If server has a tool named "get_weather", it will be
898
898
  available as "weatherget_weather"
fastmcp/tools/tool.py CHANGED
@@ -192,7 +192,7 @@ def _convert_to_content(
192
192
  other_content.append(item)
193
193
  if other_content:
194
194
  other_content = _convert_to_content(
195
- other_content, _process_as_single_item=True
195
+ other_content, serializer=serializer, _process_as_single_item=True
196
196
  )
197
197
 
198
198
  return other_content + mcp_types
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.3.0
3
+ Version: 2.3.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
@@ -76,7 +76,13 @@ fastmcp run server.py
76
76
 
77
77
  ### 📚 Documentation
78
78
 
79
- This readme provides only a high-level overview. For detailed guides, API references, and advanced patterns, please refer to the complete FastMCP documentation at **[gofastmcp.com](https://gofastmcp.com)**.
79
+ FastMCP's complete documentation is available at **[gofastmcp.com](https://gofastmcp.com)**, including detailed guides, API references, and advanced patterns. This readme provides only a high-level overview.
80
+
81
+ Documentation is also available in [llms.txt format](https://llmstxt.org/), which is a simple markdown standard that LLMs can consume easily.
82
+
83
+ There are two ways to access the LLM-friendly documentation:
84
+ - [`llms.txt`](https://gofastmcp.com/llms.txt) is essentially a sitemap, listing all the pages in the documentation.
85
+ - [`llms-full.txt`](https://gofastmcp.com/llms-full.txt) contains the entire documentation. Note this may exceed the context window of your LLM.
80
86
 
81
87
  ---
82
88
 
@@ -302,50 +308,40 @@ Learn more: [**OpenAPI Integration**](https://gofastmcp.com/patterns/openapi) |
302
308
 
303
309
  ## Running Your Server
304
310
 
305
- You can run your FastMCP server in several ways:
306
-
307
- 1. **Development (`fastmcp dev`)**: Recommended for building and testing. Provides an interactive testing environment with the MCP Inspector.
308
- ```bash
309
- fastmcp dev server.py
310
- # Optionally add temporary dependencies
311
- fastmcp dev server.py --with pandas numpy
312
- ```
313
-
314
- 2. **FastMCP CLI**: Run your server with the FastMCP CLI. This can autodetect and load your server object and run it with any transport configuration you want.
315
- ```bash
316
- fastmcp run path/to/server.py:server_object
317
-
318
- # Run as SSE on port 4200
319
- fastmcp run path/to/server.py:server_object --transport sse --port 4200
320
- ```
321
- FastMCP will auto-detect the server object if it's named `mcp`, `app`, or `server`. In these cases, you can omit the `:server_object` part unless you need to select a specific object.
322
-
323
- 3. **Direct Execution**: For maximum compatibility with the MCP ecosystem, you can run your server directly as part of a Python script. You will typically do this within an `if __name__ == "__main__":` block in your script:
324
- ```python
325
- # Add this to server.py
326
- if __name__ == "__main__":
327
- # Default: runs stdio transport
328
- mcp.run()
329
-
330
- # Example: Run with SSE transport on a specific port
331
- mcp.run(transport="sse", host="127.0.0.1", port=9000)
332
- ```
333
- Run your script:
334
- ```bash
335
- python server.py
336
- # or using uv to manage the environment
337
- uv run python server.py
338
- ```
339
- 4. **Claude Desktop Integration (`fastmcp install`)**: The easiest way to make your server persistently available in the Claude Desktop app. It handles creating an isolated environment using `uv`.
340
- ```bash
341
- fastmcp install server.py --name "My Analysis Tool"
342
- # Optionally add dependencies and environment variables
343
- fastmcp install server.py --with requests -v API_KEY=123 -f .env
344
- ```
345
-
346
-
347
- See the [**Server Documentation**](https://gofastmcp.com/servers/fastmcp#running-the-server) for more details on transports and configuration.
311
+ The main way to run a FastMCP server is by calling the `run()` method on your server instance:
312
+
313
+ ```python
314
+ # server.py
315
+ from fastmcp import FastMCP
316
+
317
+ mcp = FastMCP("Demo 🚀")
318
+
319
+ @mcp.tool()
320
+ def hello(name: str) -> str:
321
+ return f"Hello, {name}!"
322
+
323
+ if __name__ == "__main__":
324
+ mcp.run() # Default: uses STDIO transport
325
+ ```
326
+
327
+ FastMCP supports three transport protocols:
328
+
329
+ **STDIO (Default)**: Best for local tools and command-line scripts.
330
+ ```python
331
+ mcp.run(transport="stdio") # Default, so transport argument is optional
332
+ ```
333
+
334
+ **Streamable HTTP**: Recommended for web deployments.
335
+ ```python
336
+ mcp.run(transport="streamable-http", host="127.0.0.1", port=8000, path="/mcp")
337
+ ```
338
+
339
+ **SSE**: For compatibility with existing SSE clients.
340
+ ```python
341
+ mcp.run(transport="sse", host="127.0.0.1", port=8000)
342
+ ```
348
343
 
344
+ See the [**Running Server Documentation**](https://gofastmcp.com/deployment/running-server) for more details.
349
345
 
350
346
  ## Contributing
351
347
 
@@ -21,6 +21,9 @@ fastmcp/contrib/mcp_mixin/README.md,sha256=9DDTJXWkA3yv1fp5V58gofmARPQ2xWDhblYGv
21
21
  fastmcp/contrib/mcp_mixin/__init__.py,sha256=aw9IQ1ssNjCgws4ZNt8bkdpossAAGVAwwjBpMp9O5ZQ,153
22
22
  fastmcp/contrib/mcp_mixin/example.py,sha256=GnunkXmtG5hLLTUsM8aW5ZURU52Z8vI4tNLl-fK7Dg0,1228
23
23
  fastmcp/contrib/mcp_mixin/mcp_mixin.py,sha256=cfIRbnSxsVzglTD-auyTE0izVQeHP7Oz18qzYoBZJgg,7899
24
+ fastmcp/low_level/README.md,sha256=IRvElvOOc_RLLsqbUm7e6VOEwrKHPJeox0pV7JVKHWw,106
25
+ fastmcp/low_level/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ fastmcp/low_level/sse_server_transport.py,sha256=pUG3AL4Wjf9LgH9fj1l3emGEjFDFDhmKcDfgiiFJcuQ,4448
24
27
  fastmcp/prompts/__init__.py,sha256=An8uMBUh9Hrb7qqcn_5_Hent7IOeSh7EA2IUVsIrtHc,179
25
28
  fastmcp/prompts/prompt.py,sha256=psc-YiBRttbjETINaP9P9QV328yk96mDBsZgjOHVyKM,7777
26
29
  fastmcp/prompts/prompt_manager.py,sha256=9VcioLE-AoUKe1e9SynNQME9SvWy0q1QAvO1ewIWVmI,3126
@@ -32,12 +35,12 @@ fastmcp/resources/types.py,sha256=QPDeka_cM1hmvwW4FeFhqy6BEEi4MlwtpvhWUVWh5Fc,64
32
35
  fastmcp/server/__init__.py,sha256=bMD4aQD4yJqLz7-mudoNsyeV8UgQfRAg3PRwPvwTEds,119
33
36
  fastmcp/server/context.py,sha256=ykitQygA7zT5prbFTLCuYlnAzuljf_9ErUT0FYBPv3E,8135
34
37
  fastmcp/server/dependencies.py,sha256=1utkxFsV37HZcWBwI69JyngVN2ppGO_PEgxUlUHHy_Q,742
35
- fastmcp/server/http.py,sha256=yFazRx3hKLKfthcGGkeL-8gQxV0r2oGpugsuOvgjBzw,10013
38
+ fastmcp/server/http.py,sha256=esmeQZJCOxbvYBwF9gTzniymnL93n09hbjgRpmv-9bw,10076
36
39
  fastmcp/server/openapi.py,sha256=0nANnwHJ5VZInNyo2f9ErmO0K3igMv6bwyxf3G-BSls,23473
37
- fastmcp/server/proxy.py,sha256=qcBD2wWMcXA4dhqppStVH4UhsyWm0cpPUuItLpO6H6A,9621
38
- fastmcp/server/server.py,sha256=usocXySZvmrp6GOJzQQrh1lUiAyVMkRnJhk4NBhp0FQ,40827
40
+ fastmcp/server/proxy.py,sha256=LDTjzc_iQj8AldsfMU37flGRAfJic1w6qsherfyHPAA,9622
41
+ fastmcp/server/server.py,sha256=2pwYzjHFlg42Mq8qfjX5VttP1OjVJYNOCbW5H7R0hjk,40826
39
42
  fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
40
- fastmcp/tools/tool.py,sha256=lr9F90-A36Z6DT4LScJBW88uFq8xrSv00ivFxiERJe8,7786
43
+ fastmcp/tools/tool.py,sha256=HGcHjMecqAeN6eI-IfE_2UBcd1KpTV-VOTFLx9tlbpU,7809
41
44
  fastmcp/tools/tool_manager.py,sha256=p2nHyLFgz28tbsLpWOurkbWRU2Z34_HcDohjrvwjI0E,3369
42
45
  fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
43
46
  fastmcp/utilities/cache.py,sha256=aV3oZ-ZhMgLSM9iAotlUlEy5jFvGXrVo0Y5Bj4PBtqY,707
@@ -47,8 +50,8 @@ fastmcp/utilities/logging.py,sha256=zav8pnFxG_fvGJHUV2XpobmT9WVrmv1mlQBSCz-CPx4,
47
50
  fastmcp/utilities/openapi.py,sha256=Er3G1MyFwiWVxZXicXtD2j-BvttHEDTi1dgkq1KiBQc,51073
48
51
  fastmcp/utilities/tests.py,sha256=uUV-8CkhCe5zZJkxhgJXnxrjJ3Yq7cCMZN8xWKGuqdY,3181
49
52
  fastmcp/utilities/types.py,sha256=6CcqAQ1QqCO2HGSFlPS6FO5JRWnacjCcO2-EhyEnZV0,4400
50
- fastmcp-2.3.0.dist-info/METADATA,sha256=v9jZ7vkBNKfVq-L4hoGpjOMrJfi-FUs4q3Cxiqxh-zU,16396
51
- fastmcp-2.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
52
- fastmcp-2.3.0.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
53
- fastmcp-2.3.0.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
54
- fastmcp-2.3.0.dist-info/RECORD,,
53
+ fastmcp-2.3.1.dist-info/METADATA,sha256=EM2ah3-Gf3_vtum6tbRL4627SfRuugJ0xJdbsNa8EJE,15746
54
+ fastmcp-2.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
+ fastmcp-2.3.1.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
56
+ fastmcp-2.3.1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
57
+ fastmcp-2.3.1.dist-info/RECORD,,