Jeremiah Lowin commited on
Commit
88d77c1
·
unverified ·
2 Parent(s): 703f8b4884c3bb

Merge pull request #401 from jlowin/deprecate-methods

Browse files

Deprecate transport-specific methods on FastMCP server

README.md CHANGED
@@ -15,7 +15,7 @@
15
  > [!NOTE]
16
  > #### FastMCP 2.0 & The Official MCP SDK
17
  >
18
- > Recognize the `FastMCP` name? You might have used the version integrated into the [official MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk), which was based on **FastMCP 1.0**.
19
  >
20
  > **Welcome to FastMCP 2.0!** This is the actively developed successor, and it significantly expands on 1.0 by introducing powerful client capabilities, server proxying & composition, OpenAPI/FastAPI integration, and more advanced features.
21
  >
 
15
  > [!NOTE]
16
  > #### FastMCP 2.0 & The Official MCP SDK
17
  >
18
+ > Recognize the `FastMCP` name? You might have seen the version that was contributed to the [official MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk), which was based on **FastMCP 1.0**.
19
  >
20
  > **Welcome to FastMCP 2.0!** This is the actively developed successor, and it significantly expands on 1.0 by introducing powerful client capabilities, server proxying & composition, OpenAPI/FastAPI integration, and more advanced features.
21
  >
docs/deployment/asgi.mdx CHANGED
@@ -5,6 +5,9 @@ description: Integrate FastMCP servers into existing Starlette, FastAPI, or othe
5
  icon: plug
6
  ---
7
 
 
 
 
8
  While FastMCP provides standalone server capabilities, you can also integrate your FastMCP server into existing web applications. This approach is useful for:
9
 
10
  - Adding MCP functionality to an existing website or API
@@ -16,10 +19,13 @@ Please note that all FastMCP servers have a `run()` method that can be used to s
16
 
17
  ## ASGI Server
18
 
19
-
20
  FastMCP servers can be created as [Starlette](https://www.starlette.io/) ASGI apps for straightforward hosting or integration into existing applications.
21
 
22
- The first step is to obtain a Starlette application instance from your FastMCP server using either the `streamable_http_app()` (preferred) or `sse_app()` (legacy) methods:
 
 
 
 
23
 
24
  ```python
25
  from fastmcp import FastMCP
@@ -30,18 +36,23 @@ mcp = FastMCP("MyServer")
30
  def hello(name: str) -> str:
31
  return f"Hello, {name}!"
32
 
33
- # Get a Starlette app instance for the preferred transport
34
- http_app = mcp.streamable_http_app() # For Streamable HTTP transport
35
- sse_app = mcp.sse_app() # For SSE transport
 
 
36
  ```
37
 
38
- Both methods return a Starlette application that can be integrated with other ASGI-compatible web frameworks.
39
 
40
- The MCP server's endpoint is mounted at the root path `/mcp` for Streamable HTTP transport, and `/sse` for SSE transport, though you can change these paths by passing a `path` argument to the `streamable_http_app()` or `sse_app()` methods:
41
 
42
  ```python
43
- http_app = mcp.streamable_http_app(path="/custom-mcp-path")
44
- sse_app = mcp.sse_app(path="/custom-sse-path")
 
 
 
45
  ```
46
 
47
  ### Running the Server
@@ -49,9 +60,12 @@ sse_app = mcp.sse_app(path="/custom-sse-path")
49
  To run the FastMCP server, you can use the `uvicorn` ASGI server:
50
 
51
  ```python
 
52
  import uvicorn
53
 
54
- # (define the app here)
 
 
55
 
56
  if __name__ == "__main__":
57
  uvicorn.run(http_app, host="0.0.0.0", port=8000)
@@ -83,7 +97,7 @@ custom_middleware = [
83
  ]
84
 
85
  # Create ASGI app with custom middleware
86
- http_app = mcp.streamable_http_app(middleware=custom_middleware)
87
  ```
88
 
89
 
@@ -91,7 +105,7 @@ http_app = mcp.streamable_http_app(middleware=custom_middleware)
91
 
92
  <VersionBadge version="2.3.1" />
93
 
94
- You can mount your FastMCP server in another Starlette application using the `Mount` class.
95
 
96
  ```python
97
  from fastmcp import FastMCP
@@ -102,7 +116,7 @@ from starlette.routing import Mount
102
  mcp = FastMCP("MyServer")
103
 
104
  # Create the ASGI app
105
- mcp_app = mcp.streamable_http_app(path='/mcp')
106
 
107
  # Create a Starlette app and mount the MCP server
108
  app = Starlette(
@@ -134,7 +148,7 @@ from starlette.routing import Mount
134
  mcp = FastMCP("MyServer")
135
 
136
  # Create the ASGI app
137
- mcp_app = mcp.streamable_http_app(path='/mcp')
138
 
139
  # Create nested application structure
140
  inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
@@ -164,7 +178,7 @@ from starlette.routing import Mount
164
  mcp = FastMCP("MyServer")
165
 
166
  # Create the ASGI app
167
- mcp_app = mcp.streamable_http_app(path='/mcp')
168
 
169
  # Create a FastAPI app and mount the MCP server
170
  app = FastAPI(lifespan=mcp_app.router.lifespan_context)
 
5
  icon: plug
6
  ---
7
 
8
+ import { VersionBadge } from '/snippets/version-badge.mdx'
9
+
10
+
11
  While FastMCP provides standalone server capabilities, you can also integrate your FastMCP server into existing web applications. This approach is useful for:
12
 
13
  - Adding MCP functionality to an existing website or API
 
19
 
20
  ## ASGI Server
21
 
 
22
  FastMCP servers can be created as [Starlette](https://www.starlette.io/) ASGI apps for straightforward hosting or integration into existing applications.
23
 
24
+ The first step is to obtain a Starlette application instance from your FastMCP server using the `http_app()` method:
25
+
26
+ <Tip>
27
+ The `http_app()` method is new in FastMCP 2.3.2. In older versions, use `sse_app()` for SSE transport or `streamable_http_app()` for Streamable HTTP transport.
28
+ </Tip>
29
 
30
  ```python
31
  from fastmcp import FastMCP
 
36
  def hello(name: str) -> str:
37
  return f"Hello, {name}!"
38
 
39
+ # Get a Starlette app instance for Streamable HTTP transport (recommended)
40
+ http_app = mcp.http_app()
41
+
42
+ # For legacy SSE transport (deprecated)
43
+ sse_app = mcp.http_app(transport="sse")
44
  ```
45
 
46
+ Both approaches return a Starlette application that can be integrated with other ASGI-compatible web frameworks.
47
 
48
+ The MCP server's endpoint is mounted at the root path `/mcp` for Streamable HTTP transport, and `/sse` for SSE transport, though you can change these paths by passing a `path` argument to the `http_app()` method:
49
 
50
  ```python
51
+ # For Streamable HTTP transport
52
+ http_app = mcp.http_app(path="/custom-mcp-path")
53
+
54
+ # For SSE transport (deprecated)
55
+ sse_app = mcp.http_app(path="/custom-sse-path", transport="sse")
56
  ```
57
 
58
  ### Running the Server
 
60
  To run the FastMCP server, you can use the `uvicorn` ASGI server:
61
 
62
  ```python
63
+ from fastmcp import FastMCP
64
  import uvicorn
65
 
66
+ mcp = FastMCP("MyServer")
67
+
68
+ http_app = mcp.http_app()
69
 
70
  if __name__ == "__main__":
71
  uvicorn.run(http_app, host="0.0.0.0", port=8000)
 
97
  ]
98
 
99
  # Create ASGI app with custom middleware
100
+ http_app = mcp.http_app(middleware=custom_middleware)
101
  ```
102
 
103
 
 
105
 
106
  <VersionBadge version="2.3.1" />
107
 
108
+ You can mount your FastMCP server in another Starlette application:
109
 
110
  ```python
111
  from fastmcp import FastMCP
 
116
  mcp = FastMCP("MyServer")
117
 
118
  # Create the ASGI app
119
+ mcp_app = mcp.http_app(path='/mcp')
120
 
121
  # Create a Starlette app and mount the MCP server
122
  app = Starlette(
 
148
  mcp = FastMCP("MyServer")
149
 
150
  # Create the ASGI app
151
+ mcp_app = mcp.http_app(path='/mcp')
152
 
153
  # Create nested application structure
154
  inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
 
178
  mcp = FastMCP("MyServer")
179
 
180
  # Create the ASGI app
181
+ mcp_app = mcp.http_app(path='/mcp')
182
 
183
  # Create a FastAPI app and mount the MCP server
184
  app = FastAPI(lifespan=mcp_app.router.lifespan_context)
docs/deployment/running-server.mdx CHANGED
@@ -68,8 +68,8 @@ Below is a comparison of available transport options to help you choose the righ
68
  | Transport | Use Cases | Recommendation |
69
  | --------- | --------- | -------------- |
70
  | **STDIO** | Local tools, command-line scripts, and integrations with clients like Claude Desktop | Best for local tools and when clients manage server processes |
71
- | **Streamable HTTP** | Web-based deployments, microservices, exposing MCP over a network | Recommended choice for new web-based deployments |
72
- | **SSE** | Existing web-based deployments that rely on SSE | Suitable for compatibility with SSE clients; prefer Streamable HTTP for new projects |
73
 
74
  ### STDIO
75
 
@@ -92,7 +92,7 @@ When using Stdio transport, you will typically *not* run the server yourself as
92
 
93
  <VersionBadge version="2.3.0" />
94
 
95
- Streamable HTTP is a modern, efficient transport for exposing your MCP server via HTTP. It is generally recommended over SSE for new web-based deployments.
96
 
97
  To run a server using Streamable HTTP, you can use the `run()` method with the `transport` argument set to `"streamable-http"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and path (`/mcp`).
98
  <CodeGroup>
@@ -150,7 +150,12 @@ if __name__ == "__main__":
150
 
151
  ### SSE
152
 
153
- Server-Sent Events (SSE) is an HTTP-based protocol for server-to-client streaming. While FastMCP supports SSE, Streamable HTTP is preferred for new projects.
 
 
 
 
 
154
 
155
  To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse`) and message path (`/messages/`).
156
 
@@ -198,7 +203,6 @@ if __name__ == "__main__":
198
  port=4200,
199
  log_level="debug",
200
  path="/my-custom-sse-path",
201
- message_path="/my-custom-message-path/",
202
  )
203
  ```
204
  ```python {7} client.py
@@ -217,9 +221,37 @@ if __name__ == "__main__":
217
  ```
218
  </CodeGroup>
219
 
220
- Your client only needs to know the host, port, and "main" path; the message path will be transmitted to it as part of the connection handshake.
221
 
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  ## Custom Routes
225
 
 
68
  | Transport | Use Cases | Recommendation |
69
  | --------- | --------- | -------------- |
70
  | **STDIO** | Local tools, command-line scripts, and integrations with clients like Claude Desktop | Best for local tools and when clients manage server processes |
71
+ | **Streamable HTTP** | Web-based deployments, microservices, exposing MCP over a network | Recommended choice for web-based deployments |
72
+ | **SSE** | Existing web-based deployments that rely on SSE | Deprecated - prefer Streamable HTTP for new projects |
73
 
74
  ### STDIO
75
 
 
92
 
93
  <VersionBadge version="2.3.0" />
94
 
95
+ Streamable HTTP is a modern, efficient transport for exposing your MCP server via HTTP. It is the recommended transport for web-based deployments.
96
 
97
  To run a server using Streamable HTTP, you can use the `run()` method with the `transport` argument set to `"streamable-http"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and path (`/mcp`).
98
  <CodeGroup>
 
150
 
151
  ### SSE
152
 
153
+ <Warning>
154
+ The SSE transport is deprecated and may be removed in a future version.
155
+ New applications should use Streamable HTTP transport instead.
156
+ </Warning>
157
+
158
+ Server-Sent Events (SSE) is an HTTP-based protocol for server-to-client streaming. While FastMCP still supports SSE, it is deprecated and Streamable HTTP is preferred for new projects.
159
 
160
  To run a server using SSE, you can use the `run()` method with the `transport` argument set to `"sse"`. This will start a Uvicorn server on the default host (`127.0.0.1`), port (`8000`), and with default SSE path (`/sse`) and message path (`/messages/`).
161
 
 
203
  port=4200,
204
  log_level="debug",
205
  path="/my-custom-sse-path",
 
206
  )
207
  ```
208
  ```python {7} client.py
 
221
  ```
222
  </CodeGroup>
223
 
 
224
 
225
 
226
+ ## Async Usage
227
+
228
+ FastMCP provides both synchronous and asynchronous APIs for running your server. The `run()` method seen in previous examples is a synchronous method that internally uses `anyio.run()` to run the asynchronous server. For applications that are already running in an async context, FastMCP provides the `run_async()` method.
229
+
230
+ ```python {10-12}
231
+ from fastmcp import FastMCP
232
+ import asyncio
233
+
234
+ mcp = FastMCP(name="MyServer")
235
+
236
+ @mcp.tool()
237
+ def hello(name: str) -> str:
238
+ return f"Hello, {name}!"
239
+
240
+ async def main():
241
+ # Use run_async() in async contexts
242
+ await mcp.run_async(transport="streamable-http")
243
+
244
+ if __name__ == "__main__":
245
+ asyncio.run(main())
246
+ ```
247
+
248
+ <Warning>
249
+ The `run()` method cannot be called from inside an async function because it already creates its own async event loop internally. If you attempt to call `run()` from inside an async function, you'll get an error about the event loop already running.
250
+
251
+ Always use `run_async()` inside async functions and `run()` in synchronous contexts.
252
+ </Warning>
253
+
254
+ Both `run()` and `run_async()` accept the same transport arguments, so all the examples above apply to both methods.
255
 
256
  ## Custom Routes
257
 
docs/getting-started/installation.mdx CHANGED
@@ -44,7 +44,7 @@ FastMCP root path: ~/Developer/fastmcp
44
  ```
45
  ## Upgrading from the Official MCP SDK
46
 
47
- Upgrading from the official MCP SDK's FastMCP 1.0 to FastMCP 2.0 is easy! The core server API is highly compatible, so after you install the `fastmcp` package, just change your import statement from `from mcp.server.fastmcp import FastMCP` to `from fastmcp import FastMCP`.
48
 
49
 
50
  ```python {1-5}
@@ -56,8 +56,9 @@ from fastmcp import FastMCP
56
 
57
  mcp = FastMCP("My MCP Server")
58
  ```
59
-
60
- While the 1.0 server API is very stable for common use cases, FastMCP 2.0 introduces many new features (like the Client, proxying, composition) documented throughout this site. Review the documentation for details on new capabilities.
 
61
 
62
  ## Installing for Development
63
 
 
44
  ```
45
  ## Upgrading from the Official MCP SDK
46
 
47
+ Upgrading from the official MCP SDK's FastMCP 1.0 to FastMCP 2.0 is generally straightforward. The core server API is highly compatible, and in many cases, changing your import statement from `from mcp.server.fastmcp import FastMCP` to `from fastmcp import FastMCP` will be sufficient.
48
 
49
 
50
  ```python {1-5}
 
56
 
57
  mcp = FastMCP("My MCP Server")
58
  ```
59
+ <Warning>
60
+ Prior to `fastmcp==2.3.0` and `mcp==1.8.0`, the 2.x API always mirrored the 1.0 API. However, as the projects diverge, this can not be guaranteed. You may see deprecation warnings if you attempt to use 1.0 APIs in FastMCP 2.x. Please refer to this documentation for details on new capabilities.
61
+ </Warning>
62
 
63
  ## Installing for Development
64
 
docs/getting-started/welcome.mdx CHANGED
@@ -27,7 +27,7 @@ if __name__ == "__main__":
27
  ## FastMCP 2.0 and the Official MCP SDK
28
 
29
  <Tip>
30
- Recognize the `FastMCP` name? You might have used the version integrated into the [official MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk), which was based on **FastMCP 1.0**.
31
 
32
 
33
  **Welcome to FastMCP 2.0!** This is the [actively developed successor](https://github.com/jlowin/fastmcp), and it significantly expands on 1.0 by introducing powerful client capabilities, server proxying & composition, OpenAPI/FastAPI integration, and more advanced features.
 
27
  ## FastMCP 2.0 and the Official MCP SDK
28
 
29
  <Tip>
30
+ Recognize the `FastMCP` name? You might have seen the version that was contributed to the [official MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk), which was based on **FastMCP 1.0**.
31
 
32
 
33
  **Welcome to FastMCP 2.0!** This is the [actively developed successor](https://github.com/jlowin/fastmcp), and it significantly expands on 1.0 by introducing powerful client capabilities, server proxying & composition, OpenAPI/FastAPI integration, and more advanced features.
docs/servers/fastmcp.mdx CHANGED
@@ -114,11 +114,16 @@ if __name__ == "__main__":
114
  # This runs the server, defaulting to STDIO transport
115
  mcp.run()
116
 
117
- # To use a different transport, e.g., Streamable HTTP:
118
  # mcp.run(transport="streamable-http", host="127.0.0.1", port=9000)
119
  ```
120
 
121
- FastMCP supports several transport options like STDIO (default, for local tools), Streamable HTTP (recommended for web services), and SSE (legacy web transport). The server can also be run using the FastMCP CLI.
 
 
 
 
 
122
 
123
  For detailed information on each transport, how to configure them (host, port, paths), and when to use which, please refer to the [**Running Your FastMCP Server**](/deployment/running-server) guide.
124
 
 
114
  # This runs the server, defaulting to STDIO transport
115
  mcp.run()
116
 
117
+ # To use a different transport, e.g., HTTP:
118
  # mcp.run(transport="streamable-http", host="127.0.0.1", port=9000)
119
  ```
120
 
121
+ FastMCP supports several transport options:
122
+ - STDIO (default, for local tools)
123
+ - Streamable HTTP (recommended for web services)
124
+ - SSE (legacy web transport, deprecated)
125
+
126
+ The server can also be run using the FastMCP CLI.
127
 
128
  For detailed information on each transport, how to configure them (host, port, paths), and when to use which, please refer to the [**Running Your FastMCP Server**](/deployment/running-server) guide.
129
 
src/fastmcp/server/server.py CHANGED
@@ -3,6 +3,8 @@
3
  from __future__ import annotations
4
 
5
  import datetime
 
 
6
  from collections.abc import AsyncIterator, Awaitable, Callable
7
  from contextlib import (
8
  AbstractAsyncContextManager,
@@ -170,7 +172,7 @@ class FastMCP(Generic[LifespanResultT]):
170
 
171
  async def run_async(
172
  self,
173
- transport: Literal["stdio", "sse", "streamable-http"] | None = None,
174
  **transport_kwargs: Any,
175
  ) -> None:
176
  """Run the FastMCP server asynchronously.
@@ -180,19 +182,21 @@ class FastMCP(Generic[LifespanResultT]):
180
  """
181
  if transport is None:
182
  transport = "stdio"
183
- if transport not in ["stdio", "sse", "streamable-http"]:
184
  raise ValueError(f"Unknown transport: {transport}")
185
 
186
  if transport == "stdio":
187
  await self.run_stdio_async(**transport_kwargs)
 
 
188
  elif transport == "sse":
189
- await self.run_sse_async(**transport_kwargs)
190
- else: # transport == "streamable-http"
191
- await self.run_streamable_http_async(**transport_kwargs)
192
 
193
  def run(
194
  self,
195
- transport: Literal["stdio", "sse", "streamable-http"] | None = None,
196
  **transport_kwargs: Any,
197
  ) -> None:
198
  """Run the FastMCP server. Note this is a synchronous function.
@@ -714,22 +718,31 @@ class FastMCP(Generic[LifespanResultT]):
714
  self._mcp_server.create_initialization_options(),
715
  )
716
 
717
- async def run_sse_async(
718
  self,
 
719
  host: str | None = None,
720
  port: int | None = None,
721
  log_level: str | None = None,
722
  path: str | None = None,
723
- message_path: str | None = None,
724
  uvicorn_config: dict | None = None,
725
  ) -> None:
726
- """Run the server using SSE transport."""
 
 
 
 
 
 
 
 
 
727
  uvicorn_config = uvicorn_config or {}
728
- # the SSE app hangs even when a signal is sent, so we disable the
729
- # timeout to make it possible to close immediately. see
730
- # https://github.com/jlowin/fastmcp/issues/296
731
  uvicorn_config.setdefault("timeout_graceful_shutdown", 0)
732
- app = self.sse_app(path=path, message_path=message_path)
 
 
 
733
 
734
  config = uvicorn.Config(
735
  app,
@@ -741,6 +754,35 @@ class FastMCP(Generic[LifespanResultT]):
741
  server = uvicorn.Server(config)
742
  await server.serve()
743
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
744
  def sse_app(
745
  self,
746
  path: str | None = None,
@@ -755,6 +797,15 @@ class FastMCP(Generic[LifespanResultT]):
755
  message_path: The path to the message endpoint
756
  middleware: A list of middleware to apply to the app
757
  """
 
 
 
 
 
 
 
 
 
758
  return create_sse_app(
759
  server=self,
760
  message_path=message_path or self.settings.message_path,
@@ -778,20 +829,54 @@ class FastMCP(Generic[LifespanResultT]):
778
  path: The path to the StreamableHTTP endpoint
779
  middleware: A list of middleware to apply to the app
780
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
  from fastmcp.server.http import create_streamable_http_app
782
 
783
- return create_streamable_http_app(
784
- server=self,
785
- streamable_http_path=path or self.settings.streamable_http_path,
786
- event_store=None,
787
- auth_server_provider=self._auth_server_provider,
788
- auth_settings=self.settings.auth,
789
- json_response=self.settings.json_response,
790
- stateless_http=self.settings.stateless_http,
791
- debug=self.settings.debug,
792
- routes=self._additional_http_routes,
793
- middleware=middleware,
794
- )
 
 
 
 
 
 
 
 
 
 
 
 
795
 
796
  async def run_streamable_http_async(
797
  self,
@@ -801,23 +886,18 @@ class FastMCP(Generic[LifespanResultT]):
801
  path: str | None = None,
802
  uvicorn_config: dict | None = None,
803
  ) -> None:
804
- """Run the server using StreamableHTTP transport."""
805
- uvicorn_config = uvicorn_config or {}
806
- uvicorn_config.setdefault("timeout_graceful_shutdown", 0)
807
-
808
- app = self.streamable_http_app(path=path)
809
-
810
- config = uvicorn.Config(
811
- app,
812
- host=host or self.settings.host,
813
- port=port or self.settings.port,
814
- log_level=log_level or self.settings.log_level.lower(),
815
- # lifespan is required for streamable http
816
- lifespan="on",
817
- **uvicorn_config,
818
  )
819
- server = uvicorn.Server(config)
820
- await server.serve()
821
 
822
  def mount(
823
  self,
 
3
  from __future__ import annotations
4
 
5
  import datetime
6
+ import inspect
7
+ import warnings
8
  from collections.abc import AsyncIterator, Awaitable, Callable
9
  from contextlib import (
10
  AbstractAsyncContextManager,
 
172
 
173
  async def run_async(
174
  self,
175
+ transport: Literal["stdio", "streamable-http", "sse"] | None = None,
176
  **transport_kwargs: Any,
177
  ) -> None:
178
  """Run the FastMCP server asynchronously.
 
182
  """
183
  if transport is None:
184
  transport = "stdio"
185
+ if transport not in ["stdio", "streamable-http", "sse"]:
186
  raise ValueError(f"Unknown transport: {transport}")
187
 
188
  if transport == "stdio":
189
  await self.run_stdio_async(**transport_kwargs)
190
+ elif transport == "streamable-http":
191
+ await self.run_http_async(transport="streamable-http", **transport_kwargs)
192
  elif transport == "sse":
193
+ await self.run_http_async(transport="sse", **transport_kwargs)
194
+ else:
195
+ raise ValueError(f"Unknown transport: {transport}")
196
 
197
  def run(
198
  self,
199
+ transport: Literal["stdio", "streamable-http", "sse"] | None = None,
200
  **transport_kwargs: Any,
201
  ) -> None:
202
  """Run the FastMCP server. Note this is a synchronous function.
 
718
  self._mcp_server.create_initialization_options(),
719
  )
720
 
721
+ async def run_http_async(
722
  self,
723
+ transport: Literal["streamable-http", "sse"] = "streamable-http",
724
  host: str | None = None,
725
  port: int | None = None,
726
  log_level: str | None = None,
727
  path: str | None = None,
 
728
  uvicorn_config: dict | None = None,
729
  ) -> None:
730
+ """Run the server using HTTP transport.
731
+
732
+ Args:
733
+ transport: Transport protocol to use - either "streamable-http" (default) or "sse"
734
+ host: Host address to bind to (defaults to settings.host)
735
+ port: Port to bind to (defaults to settings.port)
736
+ log_level: Log level for the server (defaults to settings.log_level)
737
+ path: Path for the endpoint (defaults to settings.streamable_http_path or settings.sse_path)
738
+ uvicorn_config: Additional configuration for the Uvicorn server
739
+ """
740
  uvicorn_config = uvicorn_config or {}
 
 
 
741
  uvicorn_config.setdefault("timeout_graceful_shutdown", 0)
742
+ # lifespan is required for streamable http
743
+ uvicorn_config["lifespan"] = "on"
744
+
745
+ app = self.http_app(path=path, transport=transport)
746
 
747
  config = uvicorn.Config(
748
  app,
 
754
  server = uvicorn.Server(config)
755
  await server.serve()
756
 
757
+ async def run_sse_async(
758
+ self,
759
+ host: str | None = None,
760
+ port: int | None = None,
761
+ log_level: str | None = None,
762
+ path: str | None = None,
763
+ message_path: str | None = None,
764
+ uvicorn_config: dict | None = None,
765
+ ) -> None:
766
+ """Run the server using SSE transport."""
767
+ warnings.warn(
768
+ inspect.cleandoc(
769
+ """
770
+ The run_sse_async method is deprecated. Use run_http_async for a
771
+ modern (non-SSE) alternative, or create an SSE app with
772
+ `fastmcp.server.http.create_sse_app` and run it directly.
773
+ """
774
+ ),
775
+ DeprecationWarning,
776
+ )
777
+ await self.run_http_async(
778
+ transport="sse",
779
+ host=host,
780
+ port=port,
781
+ log_level=log_level,
782
+ path=path,
783
+ uvicorn_config=uvicorn_config,
784
+ )
785
+
786
  def sse_app(
787
  self,
788
  path: str | None = None,
 
797
  message_path: The path to the message endpoint
798
  middleware: A list of middleware to apply to the app
799
  """
800
+ warnings.warn(
801
+ inspect.cleandoc(
802
+ """
803
+ The sse_app method is deprecated. Use http_app as a modern (non-SSE)
804
+ alternative, or call `fastmcp.server.http.create_sse_app` directly.
805
+ """
806
+ ),
807
+ DeprecationWarning,
808
+ )
809
  return create_sse_app(
810
  server=self,
811
  message_path=message_path or self.settings.message_path,
 
829
  path: The path to the StreamableHTTP endpoint
830
  middleware: A list of middleware to apply to the app
831
  """
832
+ warnings.warn(
833
+ "The streamable_http_app method is deprecated. Use http_app() instead.",
834
+ DeprecationWarning,
835
+ )
836
+ return self.http_app(path=path, middleware=middleware)
837
+
838
+ def http_app(
839
+ self,
840
+ path: str | None = None,
841
+ middleware: list[Middleware] | None = None,
842
+ transport: Literal["streamable-http", "sse"] = "streamable-http",
843
+ ) -> Starlette:
844
+ """Create a Starlette app using the specified HTTP transport.
845
+
846
+ Args:
847
+ path: The path for the HTTP endpoint
848
+ middleware: A list of middleware to apply to the app
849
+ transport: Transport protocol to use - either "streamable-http" (default) or "sse"
850
+
851
+ Returns:
852
+ A Starlette application configured with the specified transport
853
+ """
854
  from fastmcp.server.http import create_streamable_http_app
855
 
856
+ if transport == "streamable-http":
857
+ return create_streamable_http_app(
858
+ server=self,
859
+ streamable_http_path=path or self.settings.streamable_http_path,
860
+ event_store=None,
861
+ auth_server_provider=self._auth_server_provider,
862
+ auth_settings=self.settings.auth,
863
+ json_response=self.settings.json_response,
864
+ stateless_http=self.settings.stateless_http,
865
+ debug=self.settings.debug,
866
+ routes=self._additional_http_routes,
867
+ middleware=middleware,
868
+ )
869
+ elif transport == "sse":
870
+ return create_sse_app(
871
+ server=self,
872
+ message_path=path or self.settings.message_path,
873
+ sse_path=path or self.settings.sse_path,
874
+ auth_server_provider=self._auth_server_provider,
875
+ auth_settings=self.settings.auth,
876
+ debug=self.settings.debug,
877
+ routes=self._additional_http_routes,
878
+ middleware=middleware,
879
+ )
880
 
881
  async def run_streamable_http_async(
882
  self,
 
886
  path: str | None = None,
887
  uvicorn_config: dict | None = None,
888
  ) -> None:
889
+ warnings.warn(
890
+ "The run_streamable_http_async method is deprecated. Use run_http_async instead.",
891
+ DeprecationWarning,
892
+ )
893
+ await self.run_http_async(
894
+ transport="streamable-http",
895
+ host=host,
896
+ port=port,
897
+ log_level=log_level,
898
+ path=path,
899
+ uvicorn_config=uvicorn_config,
 
 
 
900
  )
 
 
901
 
902
  def mount(
903
  self,
src/fastmcp/utilities/tests.py CHANGED
@@ -55,7 +55,7 @@ def temporary_settings(**kwargs: Any):
55
  def _run_server(mcp_server: FastMCP, transport: Literal["sse"], port: int) -> None:
56
  # Some Starlette apps are not pickleable, so we need to create them here based on the indicated transport
57
  if transport == "sse":
58
- app = mcp_server.sse_app()
59
  else:
60
  raise ValueError(f"Invalid transport: {transport}")
61
  uvicorn_server = uvicorn.Server(
 
55
  def _run_server(mcp_server: FastMCP, transport: Literal["sse"], port: int) -> None:
56
  # Some Starlette apps are not pickleable, so we need to create them here based on the indicated transport
57
  if transport == "sse":
58
+ app = mcp_server.http_app(transport="sse")
59
  else:
60
  raise ValueError(f"Invalid transport: {transport}")
61
  uvicorn_server = uvicorn.Server(
tests/client/test_sse.py CHANGED
@@ -58,7 +58,7 @@ def fastmcp_server():
58
 
59
  def run_server(host: str, port: int) -> None:
60
  try:
61
- app = fastmcp_server().sse_app()
62
  server = uvicorn.Server(
63
  config=uvicorn.Config(app=app, host=host, port=port, log_level="error")
64
  )
@@ -96,7 +96,7 @@ async def test_http_headers(sse_server: str):
96
 
97
  def run_nested_server(host: str, port: int) -> None:
98
  try:
99
- app = fastmcp_server().sse_app()
100
  mount = Starlette(routes=[Mount("/nest-inner", app=app)])
101
  mount2 = Starlette(routes=[Mount("/nest-outer", app=mount)])
102
  server = uvicorn.Server(
 
58
 
59
  def run_server(host: str, port: int) -> None:
60
  try:
61
+ app = fastmcp_server().http_app(transport="sse")
62
  server = uvicorn.Server(
63
  config=uvicorn.Config(app=app, host=host, port=port, log_level="error")
64
  )
 
96
 
97
  def run_nested_server(host: str, port: int) -> None:
98
  try:
99
+ app = fastmcp_server().http_app(transport="sse")
100
  mount = Starlette(routes=[Mount("/nest-inner", app=app)])
101
  mount2 = Starlette(routes=[Mount("/nest-outer", app=mount)])
102
  server = uvicorn.Server(
tests/client/test_streamable_http.py CHANGED
@@ -58,7 +58,7 @@ def fastmcp_server():
58
 
59
  def run_server(host: str, port: int) -> None:
60
  try:
61
- app = fastmcp_server().streamable_http_app()
62
  server = uvicorn.Server(
63
  config=uvicorn.Config(
64
  app=app,
@@ -106,7 +106,7 @@ async def test_http_headers(streamable_http_server: str):
106
 
107
  def run_nested_server(host: str, port: int) -> None:
108
  try:
109
- mcp_app = fastmcp_server().streamable_http_app()
110
 
111
  mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
112
  mount2 = Starlette(
 
58
 
59
  def run_server(host: str, port: int) -> None:
60
  try:
61
+ app = fastmcp_server().http_app()
62
  server = uvicorn.Server(
63
  config=uvicorn.Config(
64
  app=app,
 
106
 
107
  def run_nested_server(host: str, port: int) -> None:
108
  try:
109
+ mcp_app = fastmcp_server().http_app()
110
 
111
  mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
112
  mount2 = Starlette(
tests/server/test_http_dependencies.py CHANGED
@@ -43,7 +43,7 @@ def fastmcp_server():
43
 
44
  def run_server(host: str, port: int) -> None:
45
  try:
46
- app = fastmcp_server().streamable_http_app()
47
  server = uvicorn.Server(
48
  config=uvicorn.Config(
49
  app=app,
 
43
 
44
  def run_server(host: str, port: int) -> None:
45
  try:
46
+ app = fastmcp_server().http_app()
47
  server = uvicorn.Server(
48
  config=uvicorn.Config(
49
  app=app,
tests/server/test_http_middleware.py CHANGED
@@ -1,4 +1,4 @@
1
- """Tests for custom middleware in HTTP servers."""
2
 
3
  from collections.abc import Callable
4
  from typing import Any
@@ -68,7 +68,7 @@ async def test_sse_app_with_custom_middleware():
68
  server._additional_http_routes = routes
69
 
70
  # Create the app with custom middleware
71
- app = server.sse_app(middleware=custom_middleware)
72
 
73
  # Create a test client
74
  transport = ASGITransport(app=app)
@@ -99,7 +99,7 @@ async def test_streamable_http_app_with_custom_middleware():
99
  server._additional_http_routes = routes
100
 
101
  # Create the app with custom middleware
102
- app = server.streamable_http_app(middleware=custom_middleware)
103
 
104
  # Create a test client
105
  transport = ASGITransport(app=app)
@@ -204,7 +204,7 @@ async def test_multiple_middleware_ordering():
204
  server._additional_http_routes = routes
205
 
206
  # Create the app with custom middleware
207
- app = server.sse_app(middleware=custom_middleware)
208
 
209
  # Create a test client
210
  transport = ASGITransport(app=app)
 
1
+ """Tests for middleware in HTTP apps."""
2
 
3
  from collections.abc import Callable
4
  from typing import Any
 
68
  server._additional_http_routes = routes
69
 
70
  # Create the app with custom middleware
71
+ app = server.http_app(transport="sse", middleware=custom_middleware)
72
 
73
  # Create a test client
74
  transport = ASGITransport(app=app)
 
99
  server._additional_http_routes = routes
100
 
101
  # Create the app with custom middleware
102
+ app = server.http_app(transport="streamable-http", middleware=custom_middleware)
103
 
104
  # Create a test client
105
  transport = ASGITransport(app=app)
 
204
  server._additional_http_routes = routes
205
 
206
  # Create the app with custom middleware
207
+ app = server.http_app(transport="sse", middleware=custom_middleware)
208
 
209
  # Create a test client
210
  transport = ASGITransport(app=app)
tests/test_deprecated.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for deprecated functionality."""
2
+
3
+ import warnings
4
+ from unittest.mock import AsyncMock, patch
5
+
6
+ import pytest
7
+ from starlette.applications import Starlette
8
+
9
+ from fastmcp import FastMCP
10
+
11
+
12
+ def test_sse_app_deprecation_warning():
13
+ """Test that sse_app raises a deprecation warning."""
14
+ server = FastMCP("TestServer")
15
+
16
+ with pytest.warns(DeprecationWarning, match="The sse_app method is deprecated"):
17
+ app = server.sse_app()
18
+ assert isinstance(app, Starlette)
19
+
20
+
21
+ def test_streamable_http_app_deprecation_warning():
22
+ """Test that streamable_http_app raises a deprecation warning."""
23
+ server = FastMCP("TestServer")
24
+
25
+ with pytest.warns(
26
+ DeprecationWarning, match="The streamable_http_app method is deprecated"
27
+ ):
28
+ app = server.streamable_http_app()
29
+ assert isinstance(app, Starlette)
30
+
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_run_sse_async_deprecation_warning():
34
+ """Test that run_sse_async raises a deprecation warning."""
35
+ server = FastMCP("TestServer")
36
+
37
+ # Use patch to avoid actually running the server
38
+ with patch.object(server, "run_http_async", new_callable=AsyncMock) as mock_run:
39
+ with pytest.warns(
40
+ DeprecationWarning, match="The run_sse_async method is deprecated"
41
+ ):
42
+ await server.run_sse_async()
43
+
44
+ # Verify the mock was called with the right transport
45
+ mock_run.assert_called_once()
46
+ call_kwargs = mock_run.call_args.kwargs
47
+ assert call_kwargs.get("transport") == "sse"
48
+
49
+
50
+ @pytest.mark.asyncio
51
+ async def test_run_streamable_http_async_deprecation_warning():
52
+ """Test that run_streamable_http_async raises a deprecation warning."""
53
+ server = FastMCP("TestServer")
54
+
55
+ # Use patch to avoid actually running the server
56
+ with patch.object(server, "run_http_async", new_callable=AsyncMock) as mock_run:
57
+ with pytest.warns(
58
+ DeprecationWarning,
59
+ match="The run_streamable_http_async method is deprecated",
60
+ ):
61
+ await server.run_streamable_http_async()
62
+
63
+ # Verify the mock was called with the right transport
64
+ mock_run.assert_called_once()
65
+ call_kwargs = mock_run.call_args.kwargs
66
+ assert call_kwargs.get("transport") == "streamable-http"
67
+
68
+
69
+ def test_http_app_with_sse_transport():
70
+ """Test that http_app with SSE transport works (no warning)."""
71
+ server = FastMCP("TestServer")
72
+
73
+ # This should not raise a warning since we're using the new API
74
+ with warnings.catch_warnings(record=True) as recorded_warnings:
75
+ app = server.http_app(transport="sse")
76
+ assert isinstance(app, Starlette)
77
+
78
+ # Verify no deprecation warnings were raised for using transport parameter
79
+ deprecation_warnings = [
80
+ w for w in recorded_warnings if issubclass(w.category, DeprecationWarning)
81
+ ]
82
+ assert len(deprecation_warnings) == 0