Skip to content

Commit dc254e6

Browse files
committed
feat: emit DeprecationWarning from all SSE transport entry points
Add deprecation warnings to SseServerTransport, sse_client, MCPServer.sse_app, MCPServer.run_sse_async, and MCPServer.run(transport="sse") directing users to migrate to Streamable HTTP transport. Each warning follows the same pattern: "<name> is deprecated. Use <replacement> instead. SSE transport will be removed in a future major release." Tests verify the warnings are emitted from SseServerTransport, sse_client, and MCPServer.sse_app. A pyproject.toml filterwarnings entry prevents existing SSE tests from failing due to the new warnings. Github-Issue: #2278
1 parent 62eb08e commit dc254e6

File tree

6 files changed

+101
-4
lines changed

6 files changed

+101
-4
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ filterwarnings = [
184184
"ignore:Returning str or bytes.*:DeprecationWarning:mcp.server.lowlevel",
185185
# pywin32 internal deprecation warning
186186
"ignore:getargs.*The 'u' format is deprecated:DeprecationWarning",
187+
# SSE transport deprecation (callers should migrate to Streamable HTTP)
188+
"ignore:.*is deprecated.*Use.*instead.*SSE transport:DeprecationWarning",
187189
]
188190

189191
[tool.markdown.lint]

src/mcp/client/sse.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import warnings
23
from collections.abc import Callable
34
from contextlib import asynccontextmanager
45
from typing import Any
@@ -39,6 +40,10 @@ async def sse_client(
3940
):
4041
"""Client transport for SSE.
4142
43+
.. deprecated::
44+
The SSE transport is deprecated. Use :func:`~mcp.client.streamable_http.streamable_http_client`
45+
(Streamable HTTP) instead. SSE will be removed in a future major release.
46+
4247
`sse_read_timeout` determines how long (in seconds) the client will wait for a new
4348
event before disconnecting. All other HTTP operations are controlled by `timeout`.
4449
@@ -51,6 +56,12 @@ async def sse_client(
5156
auth: Optional HTTPX authentication handler.
5257
on_session_created: Optional callback invoked with the session ID when received.
5358
"""
59+
warnings.warn(
60+
"sse_client is deprecated. Use streamable_http_client instead. "
61+
"SSE transport will be removed in a future major release.",
62+
DeprecationWarning,
63+
stacklevel=2,
64+
)
5465
read_stream: MemoryObjectReceiveStream[SessionMessage | Exception]
5566
read_stream_writer: MemoryObjectSendStream[SessionMessage | Exception]
5667

src/mcp/server/mcpserver/server.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import inspect
77
import json
88
import re
9+
import warnings
910
from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence
1011
from contextlib import AbstractAsyncContextManager, asynccontextmanager
1112
from typing import Any, Generic, Literal, TypeVar, overload
@@ -286,6 +287,12 @@ def run(
286287
case "stdio":
287288
anyio.run(self.run_stdio_async)
288289
case "sse": # pragma: no cover
290+
warnings.warn(
291+
'run(transport="sse") is deprecated. Use run(transport="streamable-http") instead. '
292+
"SSE transport will be removed in a future major release.",
293+
DeprecationWarning,
294+
stacklevel=2,
295+
)
289296
anyio.run(lambda: self.run_sse_async(**kwargs))
290297
case "streamable-http": # pragma: no cover
291298
anyio.run(lambda: self.run_streamable_http_async(**kwargs))
@@ -854,7 +861,18 @@ async def run_sse_async( # pragma: no cover
854861
message_path: str = "/messages/",
855862
transport_security: TransportSecuritySettings | None = None,
856863
) -> None:
857-
"""Run the server using SSE transport."""
864+
"""Run the server using SSE transport.
865+
866+
.. deprecated::
867+
Use :meth:`run_streamable_http_async` instead. SSE transport will be
868+
removed in a future major release.
869+
"""
870+
warnings.warn(
871+
"run_sse_async is deprecated. Use run_streamable_http_async instead. "
872+
"SSE transport will be removed in a future major release.",
873+
DeprecationWarning,
874+
stacklevel=2,
875+
)
858876
import uvicorn
859877

860878
starlette_app = self.sse_app(
@@ -915,7 +933,18 @@ def sse_app(
915933
transport_security: TransportSecuritySettings | None = None,
916934
host: str = "127.0.0.1",
917935
) -> Starlette:
918-
"""Return an instance of the SSE server app."""
936+
"""Return an instance of the SSE server app.
937+
938+
.. deprecated::
939+
Use :meth:`streamable_http_app` instead. SSE transport will be
940+
removed in a future major release.
941+
"""
942+
warnings.warn(
943+
"sse_app is deprecated. Use streamable_http_app instead. "
944+
"SSE transport will be removed in a future major release.",
945+
DeprecationWarning,
946+
stacklevel=2,
947+
)
919948
# Auto-enable DNS rebinding protection for localhost (IPv4 and IPv6)
920949
if transport_security is None and host in ("127.0.0.1", "localhost", "::1"):
921950
transport_security = TransportSecuritySettings(

src/mcp/server/sse.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ async def handle_sse(request):
3737
"""
3838

3939
import logging
40+
import warnings
4041
from contextlib import asynccontextmanager
4142
from typing import Any
4243
from urllib.parse import quote
@@ -61,8 +62,15 @@ async def handle_sse(request):
6162

6263

6364
class SseServerTransport:
64-
"""SSE server transport for MCP. This class provides two ASGI applications,
65-
suitable for use with a framework like Starlette and a server like Hypercorn:
65+
"""SSE server transport for MCP.
66+
67+
.. deprecated::
68+
The SSE transport is deprecated. Use
69+
:class:`~mcp.server.streamable_http.StreamableHTTPServerTransport` instead.
70+
SSE will be removed in a future major release.
71+
72+
This class provides two ASGI applications, suitable for use with a framework
73+
like Starlette and a server like Hypercorn:
6674
6775
1. connect_sse() is an ASGI application which receives incoming GET requests,
6876
and sets up a new SSE stream to send server messages to the client.
@@ -97,6 +105,13 @@ def __init__(self, endpoint: str, security_settings: TransportSecuritySettings |
97105
ValueError: If the endpoint is a full URL instead of a relative path
98106
"""
99107

108+
warnings.warn(
109+
"SseServerTransport is deprecated. Use StreamableHTTPServerTransport instead. "
110+
"SSE transport will be removed in a future major release.",
111+
DeprecationWarning,
112+
stacklevel=2,
113+
)
114+
100115
super().__init__()
101116

102117
# Validate that endpoint is a relative path and not a full URL

tests/server/mcpserver/test_server.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import base64
2+
import warnings
23
from pathlib import Path
34
from typing import Any
45
from unittest.mock import AsyncMock, MagicMock, patch
@@ -82,6 +83,16 @@ async def test_sse_app_returns_starlette_app(self):
8283
assert sse_routes[0].path == "/sse"
8384
assert mount_routes[0].path == "/messages"
8485

86+
async def test_sse_app_emits_deprecation_warning(self):
87+
"""Test that sse_app emits a DeprecationWarning."""
88+
mcp = MCPServer("test")
89+
with warnings.catch_warnings(record=True) as caught:
90+
warnings.simplefilter("always")
91+
mcp.sse_app(host="0.0.0.0")
92+
93+
deprecations = [w for w in caught if issubclass(w.category, DeprecationWarning)]
94+
assert any("sse_app is deprecated" in str(w.message) for w in deprecations)
95+
8596
async def test_non_ascii_description(self):
8697
"""Test that MCPServer handles non-ASCII characters in descriptions correctly"""
8798
mcp = MCPServer()

tests/shared/test_sse.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import multiprocessing
33
import socket
4+
import warnings
45
from collections.abc import AsyncGenerator, Generator
56
from typing import Any
67
from unittest.mock import AsyncMock, MagicMock, Mock, patch
@@ -497,6 +498,34 @@ async def test_request_context_isolation(context_server: None, server_url: str)
497498
assert ctx["headers"].get("x-custom-value") == f"value-{i}"
498499

499500

501+
# --- Deprecation warning tests ---
502+
503+
504+
def test_sse_server_transport_emits_deprecation_warning():
505+
"""SseServerTransport.__init__ should emit a DeprecationWarning."""
506+
with warnings.catch_warnings(record=True) as caught:
507+
warnings.simplefilter("always")
508+
SseServerTransport("/messages/")
509+
510+
deprecations = [w for w in caught if issubclass(w.category, DeprecationWarning)]
511+
assert len(deprecations) == 1
512+
assert "SseServerTransport is deprecated" in str(deprecations[0].message)
513+
assert "StreamableHTTPServerTransport" in str(deprecations[0].message)
514+
515+
516+
@pytest.mark.anyio
517+
async def test_sse_client_emits_deprecation_warning(server: None, server_url: str):
518+
"""sse_client should emit a DeprecationWarning."""
519+
with warnings.catch_warnings(record=True) as caught:
520+
warnings.simplefilter("always")
521+
async with sse_client(server_url + "/sse") as streams:
522+
async with ClientSession(*streams) as session: # pragma: no branch
523+
await session.initialize()
524+
525+
deprecations = [w for w in caught if issubclass(w.category, DeprecationWarning)]
526+
assert any("sse_client is deprecated" in str(w.message) for w in deprecations)
527+
528+
500529
def test_sse_message_id_coercion():
501530
"""Previously, the `RequestId` would coerce a string that looked like an integer into an integer.
502531

0 commit comments

Comments
 (0)