[v1.x] Deprecate the WebSocket transport and the experimental tasks entry points (#2828)
This commit is contained in:
parent
2309e5ef97
commit
b8f491724c
7 changed files with 219 additions and 32 deletions
|
|
@ -252,7 +252,8 @@ class Experimental:
|
|||
task_group = support.task_group
|
||||
|
||||
if task_id is None:
|
||||
session_scope = self._session.experimental.task_session_scope
|
||||
features = self._session._experimental # pyright: ignore[reportPrivateUsage]
|
||||
session_scope = features.task_session_scope
|
||||
if session_scope is not None:
|
||||
task_id = scoped_task_id(session_scope)
|
||||
|
||||
|
|
|
|||
|
|
@ -488,12 +488,13 @@ class ServerTaskContext:
|
|||
create_result = CreateTaskResult.model_validate(response_data)
|
||||
client_task_id = create_result.task.taskId
|
||||
|
||||
# Poll the client's task using session.experimental
|
||||
async for _ in self._session.experimental.poll_task(client_task_id):
|
||||
# Poll the client's task using the session's experimental features
|
||||
features = self._session._experimental # pyright: ignore[reportPrivateUsage]
|
||||
async for _ in features.poll_task(client_task_id):
|
||||
pass
|
||||
|
||||
# Get final result from client
|
||||
result = await self._session.experimental.get_task_result(
|
||||
result = await features.get_task_result(
|
||||
client_task_id,
|
||||
ElicitResult,
|
||||
)
|
||||
|
|
@ -594,12 +595,13 @@ class ServerTaskContext:
|
|||
create_result = CreateTaskResult.model_validate(response_data)
|
||||
client_task_id = create_result.task.taskId
|
||||
|
||||
# Poll the client's task using session.experimental
|
||||
async for _ in self._session.experimental.poll_task(client_task_id):
|
||||
# Poll the client's task using the session's experimental features
|
||||
features = self._session._experimental # pyright: ignore[reportPrivateUsage]
|
||||
async for _ in features.poll_task(client_task_id):
|
||||
pass
|
||||
|
||||
# Get final result from client
|
||||
result = await self._session.experimental.get_task_result(
|
||||
result = await features.get_task_result(
|
||||
client_task_id,
|
||||
CreateMessageResult,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -106,8 +106,10 @@ class TaskSupport:
|
|||
stateless: Whether the session belongs to a stateless server run
|
||||
"""
|
||||
session.add_response_router(self.handler)
|
||||
if not stateless and session.experimental.task_session_scope is None:
|
||||
session.experimental.task_session_scope = new_session_scope()
|
||||
if not stateless:
|
||||
features = session._experimental # pyright: ignore[reportPrivateUsage]
|
||||
if features.task_session_scope is None:
|
||||
features.task_session_scope = new_session_scope()
|
||||
|
||||
@classmethod
|
||||
def in_memory(cls) -> "TaskSupport":
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@ class ExperimentalHandlers:
|
|||
|
||||
def _requestor_session_scope(self) -> str | None:
|
||||
"""Return the task session scope of the session making the current request."""
|
||||
return self._server.request_context.session.experimental.task_session_scope
|
||||
session = self._server.request_context.session
|
||||
return session._experimental.task_session_scope # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
def _require_task_in_requestor_scope(self, task_id: str) -> None:
|
||||
"""Reject task IDs that belong to a different session.
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ import anyio
|
|||
import jsonschema
|
||||
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
||||
from pydantic import AnyUrl
|
||||
from typing_extensions import TypeVar
|
||||
from typing_extensions import TypeVar, deprecated
|
||||
|
||||
import mcp.types as types
|
||||
from mcp.server.experimental.request_context import Experimental
|
||||
|
|
@ -244,10 +244,16 @@ class Server(Generic[LifespanResultT, RequestT]):
|
|||
return request_ctx.get()
|
||||
|
||||
@property
|
||||
@deprecated(
|
||||
"The experimental tasks API is deprecated and will be removed in mcp 2.0: tasks (SEP-1686) were removed"
|
||||
" from the MCP specification and are expected to return as a separate MCP extension."
|
||||
)
|
||||
def experimental(self) -> ExperimentalHandlers:
|
||||
"""Experimental APIs for tasks and other features.
|
||||
|
||||
WARNING: These APIs are experimental and may change without notice.
|
||||
Deprecated: the experimental tasks API will be removed in mcp 2.0. Tasks
|
||||
(SEP-1686) were removed from the MCP specification and are expected to
|
||||
return as a separate MCP extension.
|
||||
"""
|
||||
|
||||
# We create this inline so we only add these capabilities _if_ they're actually used
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import anyio
|
|||
import anyio.lowlevel
|
||||
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
||||
from pydantic import AnyUrl
|
||||
from typing_extensions import deprecated
|
||||
|
||||
import mcp.types as types
|
||||
from mcp.server.experimental.session_features import ExperimentalServerSessionFeatures
|
||||
|
|
@ -108,15 +109,26 @@ class ServerSession(
|
|||
return self._client_params # pragma: no cover
|
||||
|
||||
@property
|
||||
def experimental(self) -> ExperimentalServerSessionFeatures:
|
||||
"""Experimental APIs for server→client task operations.
|
||||
|
||||
WARNING: These APIs are experimental and may change without notice.
|
||||
"""
|
||||
def _experimental(self) -> ExperimentalServerSessionFeatures:
|
||||
"""Internal accessor for experimental features that skips the deprecation warning."""
|
||||
if self._experimental_features is None:
|
||||
self._experimental_features = ExperimentalServerSessionFeatures(self)
|
||||
return self._experimental_features
|
||||
|
||||
@property
|
||||
@deprecated(
|
||||
"The experimental tasks API is deprecated and will be removed in mcp 2.0: tasks (SEP-1686) were removed"
|
||||
" from the MCP specification and are expected to return as a separate MCP extension."
|
||||
)
|
||||
def experimental(self) -> ExperimentalServerSessionFeatures:
|
||||
"""Experimental APIs for server→client task operations.
|
||||
|
||||
Deprecated: the experimental tasks API will be removed in mcp 2.0. Tasks
|
||||
(SEP-1686) were removed from the MCP specification and are expected to
|
||||
return as a separate MCP extension.
|
||||
"""
|
||||
return self._experimental
|
||||
|
||||
def check_client_capability(self, capability: types.ClientCapabilities) -> bool: # pragma: no cover
|
||||
"""Check if the client supports a specific capability."""
|
||||
if self._client_params is None:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStre
|
|||
from pydantic_core import ValidationError
|
||||
from starlette.types import Receive, Scope, Send
|
||||
from starlette.websockets import WebSocket
|
||||
from typing_extensions import deprecated
|
||||
|
||||
import mcp.types as types
|
||||
from mcp.shared.message import SessionMessage
|
||||
|
|
@ -13,11 +14,18 @@ from mcp.shared.message import SessionMessage
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@asynccontextmanager # pragma: no cover
|
||||
@deprecated( # pragma: no cover
|
||||
"The WebSocket server transport is deprecated and will be removed in mcp 2.0. WebSocket was never part of"
|
||||
" the MCP specification; use the streamable HTTP transport instead."
|
||||
)
|
||||
@asynccontextmanager
|
||||
async def websocket_server(scope: Scope, receive: Receive, send: Send):
|
||||
"""
|
||||
WebSocket server transport for MCP. This is an ASGI application, suitable to be
|
||||
used with a framework like Starlette and a server like Hypercorn.
|
||||
|
||||
Deprecated: this transport will be removed in mcp 2.0. WebSocket was never
|
||||
part of the MCP specification; use the streamable HTTP transport instead.
|
||||
"""
|
||||
|
||||
websocket = WebSocket(scope, receive, send)
|
||||
|
|
|
|||
Loading…
Reference in a new issue