Jeremiah Lowin commited on
Commit
da4845b
·
1 Parent(s): 9663571

Deprecate transport-specific methods

Browse files
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
@@ -19,7 +19,7 @@ Please note that all FastMCP servers have a `run()` method that can be used to s
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 +30,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 +54,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 +91,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 +99,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 +110,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 +142,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 +172,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)
 
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 the `http_app()` method:
23
 
24
  ```python
25
  from fastmcp import FastMCP
 
30
  def hello(name: str) -> str:
31
  return f"Hello, {name}!"
32
 
33
+ # Get a Starlette app instance for Streamable HTTP transport (recommended)
34
+ http_app = mcp.http_app()
35
+
36
+ # For legacy SSE transport (deprecated)
37
+ sse_app = mcp.http_app(transport="sse")
38
  ```
39
 
40
+ Both approaches return a Starlette application that can be integrated with other ASGI-compatible web frameworks.
41
 
42
+ 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:
43
 
44
  ```python
45
+ # For Streamable HTTP transport
46
+ http_app = mcp.http_app(path="/custom-mcp-path")
47
+
48
+ # For SSE transport (deprecated)
49
+ sse_app = mcp.http_app(path="/custom-sse-path", transport="sse")
50
  ```
51
 
52
  ### Running the Server
 
54
  To run the FastMCP server, you can use the `uvicorn` ASGI server:
55
 
56
  ```python
57
+ from fastmcp import FastMCP
58
  import uvicorn
59
 
60
+ mcp = FastMCP("MyServer")
61
+
62
+ http_app = mcp.http_app()
63
 
64
  if __name__ == "__main__":
65
  uvicorn.run(http_app, host="0.0.0.0", port=8000)
 
91
  ]
92
 
93
  # Create ASGI app with custom middleware
94
+ http_app = mcp.http_app(middleware=custom_middleware)
95
  ```
96
 
97
 
 
99
 
100
  <VersionBadge version="2.3.1" />
101
 
102
+ You can mount your FastMCP server in another Starlette application:
103
 
104
  ```python
105
  from fastmcp import FastMCP
 
110
  mcp = FastMCP("MyServer")
111
 
112
  # Create the ASGI app
113
+ mcp_app = mcp.http_app(path='/mcp')
114
 
115
  # Create a Starlette app and mount the MCP server
116
  app = Starlette(
 
142
  mcp = FastMCP("MyServer")
143
 
144
  # Create the ASGI app
145
+ mcp_app = mcp.http_app(path='/mcp')
146
 
147
  # Create nested application structure
148
  inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
 
172
  mcp = FastMCP("MyServer")
173
 
174
  # Create the ASGI app
175
+ mcp_app = mcp.http_app(path='/mcp')
176
 
177
  # Create a FastAPI app and mount the MCP server
178
  app = FastAPI(lifespan=mcp_app.router.lifespan_context)
docs/deployment/running-server.mdx CHANGED
@@ -40,8 +40,8 @@ Below is a comparison of available transport options to help you choose the righ
40
  | Transport | Use Cases | Recommendation |
41
  | --------- | --------- | -------------- |
42
  | **STDIO** | Local tools, command-line scripts, and integrations with clients like Claude Desktop | Best for local tools and when clients manage server processes |
43
- | **Streamable HTTP** | Web-based deployments, microservices, exposing MCP over a network | Recommended choice for new web-based deployments |
44
- | **SSE** | Existing web-based deployments that rely on SSE | Suitable for compatibility with SSE clients; prefer Streamable HTTP for new projects |
45
 
46
  ### STDIO
47
 
@@ -64,7 +64,7 @@ When using Stdio transport, you will typically *not* run the server yourself as
64
 
65
  <VersionBadge version="2.3.0" />
66
 
67
- 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.
68
 
69
  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`).
70
  <CodeGroup>
@@ -122,7 +122,12 @@ if __name__ == "__main__":
122
 
123
  ### SSE
124
 
125
- 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.
 
 
 
 
 
126
 
127
  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/`).
128
 
@@ -170,7 +175,6 @@ if __name__ == "__main__":
170
  port=4200,
171
  log_level="debug",
172
  path="/my-custom-sse-path",
173
- message_path="/my-custom-message-path/",
174
  )
175
  ```
176
  ```python {7} client.py
@@ -189,9 +193,37 @@ if __name__ == "__main__":
189
  ```
190
  </CodeGroup>
191
 
192
- 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.
193
 
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
  ## Custom Routes
197
 
 
40
  | Transport | Use Cases | Recommendation |
41
  | --------- | --------- | -------------- |
42
  | **STDIO** | Local tools, command-line scripts, and integrations with clients like Claude Desktop | Best for local tools and when clients manage server processes |
43
+ | **Streamable HTTP** | Web-based deployments, microservices, exposing MCP over a network | Recommended choice for web-based deployments |
44
+ | **SSE** | Existing web-based deployments that rely on SSE | Deprecated - prefer Streamable HTTP for new projects |
45
 
46
  ### STDIO
47
 
 
64
 
65
  <VersionBadge version="2.3.0" />
66
 
67
+ Streamable HTTP is a modern, efficient transport for exposing your MCP server via HTTP. It is the recommended transport for web-based deployments.
68
 
69
  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`).
70
  <CodeGroup>
 
122
 
123
  ### SSE
124
 
125
+ <Warning>
126
+ The SSE transport is deprecated and may be removed in a future version.
127
+ New applications should use Streamable HTTP transport instead.
128
+ </Warning>
129
+
130
+ 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.
131
 
132
  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/`).
133
 
 
175
  port=4200,
176
  log_level="debug",
177
  path="/my-custom-sse-path",
 
178
  )
179
  ```
180
  ```python {7} client.py
 
193
  ```
194
  </CodeGroup>
195
 
 
196
 
197
 
198
+ ## Async Usage
199
+
200
+ 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.
201
+
202
+ ```python {10-12}
203
+ from fastmcp import FastMCP
204
+ import asyncio
205
+
206
+ mcp = FastMCP(name="MyServer")
207
+
208
+ @mcp.tool()
209
+ def hello(name: str) -> str:
210
+ return f"Hello, {name}!"
211
+
212
+ async def main():
213
+ # Use run_async() in async contexts
214
+ await mcp.run_async(transport="streamable-http")
215
+
216
+ if __name__ == "__main__":
217
+ asyncio.run(main())
218
+ ```
219
+
220
+ <Warning>
221
+ 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.
222
+
223
+ Always use `run_async()` inside async functions and `run()` in synchronous contexts.
224
+ </Warning>
225
+
226
+ Both `run()` and `run_async()` accept the same transport arguments, so all the examples above apply to both methods.
227
 
228
  ## Custom Routes
229
 
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,7 @@
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 +171,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 +181,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 +717,22 @@ 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 +744,33 @@ 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 +785,11 @@ 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 +813,44 @@ 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 +860,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 warnings
7
  from collections.abc import AsyncIterator, Awaitable, Callable
8
  from contextlib import (
9
  AbstractAsyncContextManager,
 
171
 
172
  async def run_async(
173
  self,
174
+ transport: Literal["stdio", "streamable-http", "sse"] | None = None,
175
  **transport_kwargs: Any,
176
  ) -> None:
177
  """Run the FastMCP server asynchronously.
 
181
  """
182
  if transport is None:
183
  transport = "stdio"
184
+ if transport not in ["stdio", "streamable-http", "sse"]:
185
  raise ValueError(f"Unknown transport: {transport}")
186
 
187
  if transport == "stdio":
188
  await self.run_stdio_async(**transport_kwargs)
189
+ elif transport == "streamable-http":
190
+ await self.run_http_async(transport="streamable-http", **transport_kwargs)
191
  elif transport == "sse":
192
+ await self.run_http_async(transport="sse", **transport_kwargs)
193
+ else:
194
+ raise ValueError(f"Unknown transport: {transport}")
195
 
196
  def run(
197
  self,
198
+ transport: Literal["stdio", "streamable-http", "sse"] | None = None,
199
  **transport_kwargs: Any,
200
  ) -> None:
201
  """Run the FastMCP server. Note this is a synchronous function.
 
717
  self._mcp_server.create_initialization_options(),
718
  )
719
 
720
+ async def run_http_async(
721
  self,
722
+ transport: Literal["streamable-http", "sse"] = "streamable-http",
723
  host: str | None = None,
724
  port: int | None = None,
725
  log_level: str | None = None,
726
  path: str | None = None,
 
727
  uvicorn_config: dict | None = None,
728
  ) -> None:
729
+ """Run the server using Streamable HTTP transport."""
730
  uvicorn_config = uvicorn_config or {}
 
 
 
731
  uvicorn_config.setdefault("timeout_graceful_shutdown", 0)
732
+ # lifespan is required for streamable http
733
+ uvicorn_config["lifespan"] = "on"
734
+
735
+ app = self.http_app(path=path, transport=transport)
736
 
737
  config = uvicorn.Config(
738
  app,
 
744
  server = uvicorn.Server(config)
745
  await server.serve()
746
 
747
+ async def run_sse_async(
748
+ self,
749
+ host: str | None = None,
750
+ port: int | None = None,
751
+ log_level: str | None = None,
752
+ path: str | None = None,
753
+ message_path: str | None = None,
754
+ uvicorn_config: dict | None = None,
755
+ ) -> None:
756
+ """Run the server using SSE transport."""
757
+ warnings.warn(
758
+ """
759
+ The run_sse_async method is deprecated. Use run_http_async for a
760
+ modern (non-SSE) alternative, or create an SSE app with
761
+ `fastmcp.server.http.create_sse_app` and run it directly.
762
+ """,
763
+ DeprecationWarning,
764
+ )
765
+ await self.run_http_async(
766
+ transport="sse",
767
+ host=host,
768
+ port=port,
769
+ log_level=log_level,
770
+ path=path,
771
+ uvicorn_config=uvicorn_config,
772
+ )
773
+
774
  def sse_app(
775
  self,
776
  path: str | None = None,
 
785
  message_path: The path to the message endpoint
786
  middleware: A list of middleware to apply to the app
787
  """
788
+ warnings.warn(
789
+ """The sse_app method is deprecated. Use http_app as a modern (non-SSE)
790
+ alternative, or call `fastmcp.server.http.create_sse_app` directly.""",
791
+ DeprecationWarning,
792
+ )
793
  return create_sse_app(
794
  server=self,
795
  message_path=message_path or self.settings.message_path,
 
813
  path: The path to the StreamableHTTP endpoint
814
  middleware: A list of middleware to apply to the app
815
  """
816
+ warnings.warn(
817
+ "The streamable_http_app method is deprecated. Use http_app() instead.",
818
+ DeprecationWarning,
819
+ )
820
+ return self.http_app(path=path, middleware=middleware)
821
+
822
+ def http_app(
823
+ self,
824
+ path: str | None = None,
825
+ middleware: list[Middleware] | None = None,
826
+ transport: Literal["streamable-http", "sse"] = "streamable-http",
827
+ ) -> Starlette:
828
  from fastmcp.server.http import create_streamable_http_app
829
 
830
+ if transport == "streamable-http":
831
+ return create_streamable_http_app(
832
+ server=self,
833
+ streamable_http_path=path or self.settings.streamable_http_path,
834
+ event_store=None,
835
+ auth_server_provider=self._auth_server_provider,
836
+ auth_settings=self.settings.auth,
837
+ json_response=self.settings.json_response,
838
+ stateless_http=self.settings.stateless_http,
839
+ debug=self.settings.debug,
840
+ routes=self._additional_http_routes,
841
+ middleware=middleware,
842
+ )
843
+ elif transport == "sse":
844
+ return create_sse_app(
845
+ server=self,
846
+ message_path=path or self.settings.message_path,
847
+ sse_path=path or self.settings.sse_path,
848
+ auth_server_provider=self._auth_server_provider,
849
+ auth_settings=self.settings.auth,
850
+ debug=self.settings.debug,
851
+ routes=self._additional_http_routes,
852
+ middleware=middleware,
853
+ )
854
 
855
  async def run_streamable_http_async(
856
  self,
 
860
  path: str | None = None,
861
  uvicorn_config: dict | None = None,
862
  ) -> None:
863
+ warnings.warn(
864
+ "The run_streamable_http_async method is deprecated. Use run_http_async instead.",
865
+ DeprecationWarning,
866
+ )
867
+ await self.run_http_async(
868
+ transport="streamable-http",
869
+ host=host,
870
+ port=port,
871
+ log_level=log_level,
872
+ path=path,
873
+ uvicorn_config=uvicorn_config,
 
 
 
874
  )
 
 
875
 
876
  def mount(
877
  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