fix: add StatelessModeNotSupported exception and improve tests (#1828)

This commit is contained in:
Max Isbey 2026-01-06 11:26:26 +00:00 • committed by GitHub
commit 37de50144f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 132 additions and 215 deletions

View file

@ -49,6 +49,7 @@ import mcp.types as types
from mcp.server.experimental.session_features import ExperimentalServerSessionFeatures
from mcp.server.models import InitializationOptions
from mcp.server.validation import validate_sampling_tools, validate_tool_use_result_messages
from mcp.shared.exceptions import StatelessModeNotSupported
from mcp.shared.experimental.tasks.capabilities import check_tasks_capability
from mcp.shared.experimental.tasks.helpers import RELATED_TASK_METADATA_KEY
from mcp.shared.message import ServerMessageMetadata, SessionMessage
@ -157,26 +158,6 @@ class ServerSession(
return True
def _require_stateful_mode(self, feature_name: str) -> None:
"""Raise an error if trying to use a feature that requires stateful mode.
Server-to-client requests (sampling, elicitation, list_roots) are not
supported in stateless HTTP mode because there is no persistent connection
for bidirectional communication.
Args:
feature_name: Name of the feature being used (for error message)
Raises:
RuntimeError: If the session is in stateless mode
"""
if self._stateless:
raise RuntimeError(
f"Cannot use {feature_name} in stateless HTTP mode. "
"Stateless mode does not support server-to-client requests. "
"Use stateful mode (stateless_http=False) to enable this feature."
)
async def _receive_loop(self) -> None:
async with self._incoming_message_stream_writer:
await super()._receive_loop()
@ -332,9 +313,10 @@ class ServerSession(
Raises:
McpError: If tools are provided but client doesn't support them.
ValueError: If tool_use or tool_result message structure is invalid.
RuntimeError: If called in stateless HTTP mode.
StatelessModeNotSupported: If called in stateless HTTP mode.
"""
self._require_stateful_mode("sampling")
if self._stateless:
raise StatelessModeNotSupported(method="sampling")
client_caps = self._client_params.capabilities if self._client_params else None
validate_sampling_tools(client_caps, tools, tool_choice)
validate_tool_use_result_messages(messages)
@ -372,7 +354,8 @@ class ServerSession(
async def list_roots(self) -> types.ListRootsResult:
"""Send a roots/list request."""
self._require_stateful_mode("list_roots")
if self._stateless:
raise StatelessModeNotSupported(method="list_roots")
return await self.send_request(
types.ServerRequest(types.ListRootsRequest()),
types.ListRootsResult,
@ -417,9 +400,10 @@ class ServerSession(
The client's response with form data
Raises:
RuntimeError: If called in stateless HTTP mode.
StatelessModeNotSupported: If called in stateless HTTP mode.
"""
self._require_stateful_mode("elicitation")
if self._stateless:
raise StatelessModeNotSupported(method="elicitation")
return await self.send_request(
types.ServerRequest(
types.ElicitRequest(
@ -455,9 +439,10 @@ class ServerSession(
The client's response indicating acceptance, decline, or cancellation
Raises:
RuntimeError: If called in stateless HTTP mode.
StatelessModeNotSupported: If called in stateless HTTP mode.
"""
self._require_stateful_mode("elicitation")
if self._stateless:
raise StatelessModeNotSupported(method="elicitation")
return await self.send_request(
types.ServerRequest(
types.ElicitRequest(

View file

@ -18,6 +18,24 @@ class McpError(Exception):
self.error = error
class StatelessModeNotSupported(RuntimeError):
"""
Raised when attempting to use a method that is not supported in stateless mode.
Server-to-client requests (sampling, elicitation, list_roots) are not
supported in stateless HTTP mode because there is no persistent connection
for bidirectional communication.
"""
def __init__(self, method: str):
super().__init__(
f"Cannot use {method} in stateless HTTP mode. "
"Stateless mode does not support server-to-client requests. "
"Use stateful mode (stateless_http=False) to enable this feature."
)
self.method = method
class UrlElicitationRequiredError(McpError):
"""
Specialized error for when a tool requires URL mode elicitation(s) before proceeding.