Deprecate Server.__init__ handlers for removed capabilities (#3002)
This commit is contained in:
committed by
GitHub
parent
08b62308d4
commit
ecdf09d44f
@@ -1345,6 +1345,8 @@ The user-facing methods for these features now carry `typing_extensions.deprecat
|
||||
- Roots: `ServerSession.list_roots()`, `ClientPeer.list_roots()`, `ClientSession.send_roots_list_changed()`, `Client.send_roots_list_changed()`
|
||||
- Logging: `ServerSession.send_log_message()`, `Connection.log()`, `ClientSession.set_logging_level()`, `Client.set_logging_level()`, `mcp.server.context.Context.log()` (the lowlevel `Context`), and the `MCPServer` `Context` helpers `log()`, `debug()`, `info()`, `warning()`, `error()`
|
||||
|
||||
Registering a handler for a deprecated capability is deprecated too. The `Server.__init__` parameters `on_set_logging_level` (Logging) and `on_roots_list_changed` (Roots) are now split out into a `typing_extensions.deprecated` overload, so passing either is flagged by type checkers and emits `mcp.MCPDeprecationWarning` at construction time. `on_progress` follows the same pattern (see below). The non-deprecated overload omits these parameters, so the common case stays warning-free.
|
||||
|
||||
The runtime warning is emitted as `mcp.MCPDeprecationWarning`, which subclasses `UserWarning` (not `DeprecationWarning`) so it is visible by default. To silence it, filter that category:
|
||||
|
||||
```python
|
||||
|
||||
@@ -57,7 +57,7 @@ def build_server() -> Server[Any]:
|
||||
"""Registered so the server advertises the `logging` capability; never called."""
|
||||
raise NotImplementedError
|
||||
|
||||
return Server(
|
||||
return Server( # pyright: ignore[reportDeprecated]
|
||||
"streaming-example",
|
||||
on_list_tools=list_tools,
|
||||
on_call_tool=call_tool,
|
||||
|
||||
@@ -37,11 +37,12 @@ handler callables by method string.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import warnings
|
||||
from collections.abc import AsyncIterator, Awaitable, Callable
|
||||
from contextlib import AbstractAsyncContextManager, asynccontextmanager
|
||||
from dataclasses import dataclass
|
||||
from importlib.metadata import version as importlib_version
|
||||
from typing import Any, Generic
|
||||
from typing import Any, Generic, overload
|
||||
|
||||
import mcp_types as types
|
||||
from mcp_types.version import MODERN_PROTOCOL_VERSIONS
|
||||
@@ -50,7 +51,7 @@ from starlette.applications import Starlette
|
||||
from starlette.middleware import Middleware
|
||||
from starlette.middleware.authentication import AuthenticationMiddleware
|
||||
from starlette.routing import Mount, Route
|
||||
from typing_extensions import TypeVar
|
||||
from typing_extensions import TypeVar, deprecated
|
||||
|
||||
from mcp.server._otel import OpenTelemetryMiddleware
|
||||
from mcp.server.auth.middleware.auth_context import AuthContextMiddleware
|
||||
@@ -65,6 +66,7 @@ from mcp.server.streamable_http import EventStore
|
||||
from mcp.server.streamable_http_manager import StreamableHTTPASGIApp, StreamableHTTPSessionManager
|
||||
from mcp.server.transport_security import TransportSecuritySettings
|
||||
from mcp.shared._stream_protocols import ReadStream, WriteStream
|
||||
from mcp.shared.exceptions import MCPDeprecationWarning
|
||||
from mcp.shared.message import SessionMessage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -127,6 +129,89 @@ def _package_version(package: str) -> str:
|
||||
|
||||
|
||||
class Server(Generic[LifespanResultT]):
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
*,
|
||||
version: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
instructions: str | None = None,
|
||||
website_url: str | None = None,
|
||||
icons: list[types.Icon] | None = None,
|
||||
lifespan: Callable[
|
||||
[Server[LifespanResultT]],
|
||||
AbstractAsyncContextManager[LifespanResultT],
|
||||
] = lifespan,
|
||||
# Request handlers
|
||||
on_list_tools: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None],
|
||||
Awaitable[types.ListToolsResult],
|
||||
]
|
||||
| None = None,
|
||||
on_call_tool: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.CallToolRequestParams],
|
||||
Awaitable[types.CallToolResult | types.InputRequiredResult],
|
||||
]
|
||||
| None = None,
|
||||
on_list_resources: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None],
|
||||
Awaitable[types.ListResourcesResult],
|
||||
]
|
||||
| None = None,
|
||||
on_list_resource_templates: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None],
|
||||
Awaitable[types.ListResourceTemplatesResult],
|
||||
]
|
||||
| None = None,
|
||||
on_read_resource: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.ReadResourceRequestParams],
|
||||
Awaitable[types.ReadResourceResult | types.InputRequiredResult],
|
||||
]
|
||||
| None = None,
|
||||
on_subscribe_resource: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.SubscribeRequestParams],
|
||||
Awaitable[types.EmptyResult],
|
||||
]
|
||||
| None = None,
|
||||
on_unsubscribe_resource: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.UnsubscribeRequestParams],
|
||||
Awaitable[types.EmptyResult],
|
||||
]
|
||||
| None = None,
|
||||
on_subscriptions_listen: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.SubscriptionsListenRequestParams],
|
||||
Awaitable[types.EmptyResult],
|
||||
]
|
||||
| None = None,
|
||||
on_list_prompts: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None],
|
||||
Awaitable[types.ListPromptsResult],
|
||||
]
|
||||
| None = None,
|
||||
on_get_prompt: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.GetPromptRequestParams],
|
||||
Awaitable[types.GetPromptResult | types.InputRequiredResult],
|
||||
]
|
||||
| None = None,
|
||||
on_completion: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.CompleteRequestParams],
|
||||
Awaitable[types.CompleteResult],
|
||||
]
|
||||
| None = None,
|
||||
on_ping: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.RequestParams | None],
|
||||
Awaitable[types.EmptyResult],
|
||||
] = _ping_handler,
|
||||
) -> None: ...
|
||||
@overload
|
||||
@deprecated(
|
||||
"on_set_logging_level (Logging) and on_roots_list_changed (Roots) are deprecated as of 2026-07-28 "
|
||||
"(SEP-2577); on_progress (client-to-server progress) is deprecated as of 2026-07-28. Passing any of "
|
||||
"them emits an MCPDeprecationWarning at runtime.",
|
||||
category=MCPDeprecationWarning,
|
||||
)
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
@@ -217,7 +302,117 @@ class Server(Generic[LifespanResultT]):
|
||||
Awaitable[None],
|
||||
]
|
||||
| None = None,
|
||||
):
|
||||
) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
*,
|
||||
version: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
instructions: str | None = None,
|
||||
website_url: str | None = None,
|
||||
icons: list[types.Icon] | None = None,
|
||||
lifespan: Callable[
|
||||
[Server[LifespanResultT]],
|
||||
AbstractAsyncContextManager[LifespanResultT],
|
||||
] = lifespan,
|
||||
# Request handlers
|
||||
on_list_tools: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None],
|
||||
Awaitable[types.ListToolsResult],
|
||||
]
|
||||
| None = None,
|
||||
on_call_tool: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.CallToolRequestParams],
|
||||
Awaitable[types.CallToolResult | types.InputRequiredResult],
|
||||
]
|
||||
| None = None,
|
||||
on_list_resources: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None],
|
||||
Awaitable[types.ListResourcesResult],
|
||||
]
|
||||
| None = None,
|
||||
on_list_resource_templates: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None],
|
||||
Awaitable[types.ListResourceTemplatesResult],
|
||||
]
|
||||
| None = None,
|
||||
on_read_resource: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.ReadResourceRequestParams],
|
||||
Awaitable[types.ReadResourceResult | types.InputRequiredResult],
|
||||
]
|
||||
| None = None,
|
||||
on_subscribe_resource: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.SubscribeRequestParams],
|
||||
Awaitable[types.EmptyResult],
|
||||
]
|
||||
| None = None,
|
||||
on_unsubscribe_resource: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.UnsubscribeRequestParams],
|
||||
Awaitable[types.EmptyResult],
|
||||
]
|
||||
| None = None,
|
||||
on_subscriptions_listen: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.SubscriptionsListenRequestParams],
|
||||
Awaitable[types.EmptyResult],
|
||||
]
|
||||
| None = None,
|
||||
on_list_prompts: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.PaginatedRequestParams | None],
|
||||
Awaitable[types.ListPromptsResult],
|
||||
]
|
||||
| None = None,
|
||||
on_get_prompt: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.GetPromptRequestParams],
|
||||
Awaitable[types.GetPromptResult | types.InputRequiredResult],
|
||||
]
|
||||
| None = None,
|
||||
on_completion: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.CompleteRequestParams],
|
||||
Awaitable[types.CompleteResult],
|
||||
]
|
||||
| None = None,
|
||||
on_set_logging_level: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.SetLevelRequestParams],
|
||||
Awaitable[types.EmptyResult],
|
||||
]
|
||||
| None = None,
|
||||
on_ping: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.RequestParams | None],
|
||||
Awaitable[types.EmptyResult],
|
||||
] = _ping_handler,
|
||||
# Notification handlers
|
||||
on_roots_list_changed: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.NotificationParams | None],
|
||||
Awaitable[None],
|
||||
]
|
||||
| None = None,
|
||||
on_progress: Callable[
|
||||
[ServerRequestContext[LifespanResultT], types.ProgressNotificationParams],
|
||||
Awaitable[None],
|
||||
]
|
||||
| None = None,
|
||||
) -> None:
|
||||
if on_set_logging_level is not None:
|
||||
warnings.warn(
|
||||
"The logging capability is deprecated as of 2026-07-28 (SEP-2577).",
|
||||
MCPDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
if on_roots_list_changed is not None:
|
||||
warnings.warn(
|
||||
"The roots capability is deprecated as of 2026-07-28 (SEP-2577).",
|
||||
MCPDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
if on_progress is not None:
|
||||
warnings.warn(
|
||||
"Client-to-server progress is deprecated as of 2026-07-28.",
|
||||
MCPDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.title = title
|
||||
|
||||
@@ -75,7 +75,7 @@ def simple_server() -> Server:
|
||||
async def handle_completion(ctx: ServerRequestContext, params: types.CompleteRequestParams) -> types.CompleteResult:
|
||||
return types.CompleteResult(completion=types.Completion(values=[]))
|
||||
|
||||
return Server(
|
||||
return Server( # pyright: ignore[reportDeprecated]
|
||||
name="test_server",
|
||||
on_list_resources=handle_list_resources,
|
||||
on_subscribe_resource=handle_subscribe_resource,
|
||||
@@ -279,7 +279,7 @@ async def test_client_send_progress_notification():
|
||||
received_from_client = {"progress_token": params.progress_token, "progress": params.progress}
|
||||
event.set()
|
||||
|
||||
server = Server(name="test_server", on_progress=handle_progress)
|
||||
server = Server(name="test_server", on_progress=handle_progress) # pyright: ignore[reportDeprecated]
|
||||
|
||||
with anyio.fail_after(5):
|
||||
async with Client(server, mode="legacy") as client:
|
||||
|
||||
@@ -173,7 +173,7 @@ async def test_a_tool_rejected_with_url_elicitation_required_succeeds_on_retry_a
|
||||
"""Registered so the logging capability is advertised; the client never sets a level."""
|
||||
raise NotImplementedError
|
||||
|
||||
server = Server(
|
||||
server = Server( # pyright: ignore[reportDeprecated]
|
||||
"gatekeeper",
|
||||
on_list_tools=_list_tools("read_files"),
|
||||
on_call_tool=call_tool,
|
||||
|
||||
@@ -127,7 +127,7 @@ async def test_initialize_capabilities_reflect_registered_handlers(connect: Conn
|
||||
"""Registered only so the completions capability is advertised; never called."""
|
||||
raise NotImplementedError
|
||||
|
||||
server = Server(
|
||||
server = Server( # pyright: ignore[reportDeprecated]
|
||||
"full",
|
||||
on_list_tools=list_tools,
|
||||
on_list_resources=list_resources,
|
||||
|
||||
@@ -36,7 +36,7 @@ async def test_set_logging_level_reaches_handler(connect: Connect) -> None:
|
||||
assert params.level == "warning"
|
||||
return EmptyResult()
|
||||
|
||||
server = Server("logger", on_set_logging_level=set_logging_level)
|
||||
server = Server("logger", on_set_logging_level=set_logging_level) # pyright: ignore[reportDeprecated]
|
||||
|
||||
async with connect(server) as client:
|
||||
result = await client.set_logging_level("warning") # pyright: ignore[reportDeprecated]
|
||||
@@ -76,7 +76,9 @@ async def test_log_messages_reach_logging_callback_in_order(connect: Connect) ->
|
||||
"""Registered so the logging capability is advertised; the client never sets a level."""
|
||||
raise NotImplementedError
|
||||
|
||||
server = Server("logger", on_list_tools=list_tools, on_call_tool=call_tool, on_set_logging_level=set_logging_level)
|
||||
server = Server( # pyright: ignore[reportDeprecated]
|
||||
"logger", on_list_tools=list_tools, on_call_tool=call_tool, on_set_logging_level=set_logging_level
|
||||
)
|
||||
|
||||
async with connect(server, logging_callback=collect) as client:
|
||||
result = await client.call_tool("chatty", {})
|
||||
@@ -115,7 +117,9 @@ async def test_log_messages_at_every_severity_level(connect: Connect) -> None:
|
||||
"""Registered so the logging capability is advertised; the client never sets a level."""
|
||||
raise NotImplementedError
|
||||
|
||||
server = Server("logger", on_list_tools=list_tools, on_call_tool=call_tool, on_set_logging_level=set_logging_level)
|
||||
server = Server( # pyright: ignore[reportDeprecated]
|
||||
"logger", on_list_tools=list_tools, on_call_tool=call_tool, on_set_logging_level=set_logging_level
|
||||
)
|
||||
|
||||
async with connect(server, logging_callback=collect) as client:
|
||||
await client.call_tool("siren", {})
|
||||
|
||||
@@ -118,7 +118,7 @@ async def test_client_progress_notification_reaches_server_handler(connect: Conn
|
||||
received.append(params)
|
||||
delivered.set()
|
||||
|
||||
server = Server("observer", on_progress=on_progress)
|
||||
server = Server("observer", on_progress=on_progress) # pyright: ignore[reportDeprecated]
|
||||
|
||||
async with connect(server) as client:
|
||||
await client.send_progress_notification("upload-1", 0.5, total=1.0, message="halfway") # pyright: ignore[reportDeprecated]
|
||||
|
||||
@@ -153,7 +153,7 @@ async def test_roots_list_changed_reaches_server_handler(connect: Connect) -> No
|
||||
received.append(params)
|
||||
delivered.set()
|
||||
|
||||
server = Server("rooted", on_roots_list_changed=roots_list_changed)
|
||||
server = Server("rooted", on_roots_list_changed=roots_list_changed) # pyright: ignore[reportDeprecated]
|
||||
|
||||
async def list_roots(context: ClientRequestContext) -> ListRootsResult:
|
||||
"""Registered so the client declares the roots capability; the server never asks for roots."""
|
||||
|
||||
@@ -262,7 +262,7 @@ async def test_set_level_with_an_unrecognized_value_is_answered_with_invalid_par
|
||||
"""Registered so the logging capability is advertised; never called -- params validation fails first."""
|
||||
raise NotImplementedError
|
||||
|
||||
server = Server("logger", on_set_logging_level=set_logging_level)
|
||||
server = Server("logger", on_set_logging_level=set_logging_level) # pyright: ignore[reportDeprecated]
|
||||
errors: list[ErrorData] = []
|
||||
|
||||
async with create_client_server_memory_streams() as (client_streams, server_streams):
|
||||
|
||||
@@ -53,7 +53,11 @@ async def set_logging_level(ctx: ServerRequestContext, params: SetLevelRequestPa
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
server = Server("stdio-echo", on_list_tools=list_tools, on_call_tool=call_tool, on_set_logging_level=set_logging_level)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", MCPDeprecationWarning)
|
||||
server = Server( # pyright: ignore[reportDeprecated]
|
||||
"stdio-echo", on_list_tools=list_tools, on_call_tool=call_tool, on_set_logging_level=set_logging_level
|
||||
)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
@@ -73,7 +73,7 @@ def _server() -> Server:
|
||||
"""Registered so the resources subscribe sub-capability is advertised; the client never subscribes."""
|
||||
raise NotImplementedError
|
||||
|
||||
return Server(
|
||||
return Server( # pyright: ignore[reportDeprecated]
|
||||
"hosted",
|
||||
on_list_tools=list_tools,
|
||||
on_call_tool=call_tool,
|
||||
|
||||
Reference in New Issue
Block a user