From b70d1301d06e5fcddc069e53355efb1d3ac28f50 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Thu, 19 Mar 2026 11:14:32 +0000 Subject: [PATCH] Mark SSE transport as deprecated in favor of Streamable HTTP Add DeprecationWarning and docstring deprecation notices to: - sse_client() in client/sse.py - SseServerTransport in server/sse.py - MCPServer.run(transport="sse"), run_sse_async(), and sse_app() The HTTP+SSE transport was replaced by Streamable HTTP in protocol revision 2025-03-26. The TypeScript SDK already marks SSEClientTransport as deprecated; this brings the Python SDK into alignment. Closes #2278 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/mcp/client/sse.py | 14 ++++++++++++++ src/mcp/server/mcpserver/server.py | 30 ++++++++++++++++++++++++++++-- src/mcp/server/sse.py | 28 ++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index 7b66b5c1b..6f9fffa4b 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -1,4 +1,5 @@ import logging +import warnings from collections.abc import Callable from contextlib import asynccontextmanager from typing import Any @@ -39,6 +40,12 @@ async def sse_client( ): """Client transport for SSE. + .. deprecated:: + sse_client is deprecated. Prefer to use + :func:`streamablehttp_client` where possible instead. + Note that because some servers still use SSE, clients may need + to support both transports during the migration period. + `sse_read_timeout` determines how long (in seconds) the client will wait for a new event before disconnecting. All other HTTP operations are controlled by `timeout`. @@ -51,6 +58,13 @@ async def sse_client( auth: Optional HTTPX authentication handler. on_session_created: Optional callback invoked with the session ID when received. """ + warnings.warn( + "sse_client is deprecated. Prefer to use streamablehttp_client where possible " + "instead. Note that because some servers still use SSE, clients may need to " + "support both transports during the migration period.", + DeprecationWarning, + stacklevel=2, + ) read_stream: MemoryObjectReceiveStream[SessionMessage | Exception] read_stream_writer: MemoryObjectSendStream[SessionMessage | Exception] diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index 2a7a58117..6b0543f37 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -6,6 +6,7 @@ import inspect import json import re +import warnings from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence from contextlib import AbstractAsyncContextManager, asynccontextmanager from typing import Any, Generic, Literal, TypeVar, overload @@ -286,6 +287,11 @@ def run( case "stdio": anyio.run(self.run_stdio_async) case "sse": # pragma: no cover + warnings.warn( + 'transport="sse" is deprecated. Use transport="streamable-http" instead.', + DeprecationWarning, + stacklevel=2, + ) anyio.run(lambda: self.run_sse_async(**kwargs)) case "streamable-http": # pragma: no cover anyio.run(lambda: self.run_streamable_http_async(**kwargs)) @@ -854,7 +860,17 @@ async def run_sse_async( # pragma: no cover message_path: str = "/messages/", transport_security: TransportSecuritySettings | None = None, ) -> None: - """Run the server using SSE transport.""" + """Run the server using SSE transport. + + .. deprecated:: + SSE transport is deprecated. Prefer to use + :meth:`run_streamable_http_async` instead. + """ + warnings.warn( + "run_sse_async is deprecated. Use run_streamable_http_async instead.", + DeprecationWarning, + stacklevel=2, + ) import uvicorn starlette_app = self.sse_app( @@ -915,7 +931,17 @@ def sse_app( transport_security: TransportSecuritySettings | None = None, host: str = "127.0.0.1", ) -> Starlette: - """Return an instance of the SSE server app.""" + """Return an instance of the SSE server app. + + .. deprecated:: + SSE transport is deprecated. Prefer to use + :meth:`streamable_http_app` instead. + """ + warnings.warn( + "sse_app is deprecated. Use streamable_http_app instead.", + DeprecationWarning, + stacklevel=2, + ) # Auto-enable DNS rebinding protection for localhost (IPv4 and IPv6) if transport_security is None and host in ("127.0.0.1", "localhost", "::1"): transport_security = TransportSecuritySettings( diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index 9dcee67f7..1c418098a 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -1,4 +1,10 @@ -"""SSE Server Transport Module +"""SSE Server Transport Module (Deprecated) + +.. deprecated:: + The HTTP+SSE transport was replaced by Streamable HTTP in the + `2025-03-26 `_ + protocol revision. Use :class:`mcp.server.streamable_http.StreamableHTTPServerTransport` + instead. SSE is retained for backwards compatibility with older clients. This module implements a Server-Sent Events (SSE) transport layer for MCP servers. @@ -37,6 +43,7 @@ async def handle_sse(request): """ import logging +import warnings from contextlib import asynccontextmanager from typing import Any from urllib.parse import quote @@ -61,7 +68,15 @@ async def handle_sse(request): class SseServerTransport: - """SSE server transport for MCP. This class provides two ASGI applications, + """SSE server transport for MCP. + + .. deprecated:: + ``SseServerTransport`` is deprecated. Prefer to use + :class:`~mcp.server.streamable_http.StreamableHTTPServerTransport` + where possible instead. Note that because some clients still use SSE, + servers may need to support both transports during the migration period. + + This class provides two ASGI applications, suitable for use with a framework like Starlette and a server like Hypercorn: 1. connect_sse() is an ASGI application which receives incoming GET requests, @@ -99,6 +114,15 @@ def __init__(self, endpoint: str, security_settings: TransportSecuritySettings | super().__init__() + warnings.warn( + "SseServerTransport is deprecated. Prefer to use " + "StreamableHTTPServerTransport where possible instead. Note that because " + "some clients still use SSE, servers may need to support both transports " + "during the migration period.", + DeprecationWarning, + stacklevel=2, + ) + # Validate that endpoint is a relative path and not a full URL if "://" in endpoint or endpoint.startswith("//") or "?" in endpoint or "#" in endpoint: raise ValueError(