Skip to content

Commit a5c1522

Browse files
committed
Emit deprecation warnings as a visible MCPDeprecationWarning
The SEP-2577 method deprecations now warn via `mcp.MCPDeprecationWarning`, a `UserWarning` subclass, instead of the built-in `DeprecationWarning`. Python silences `DeprecationWarning` by default outside `__main__`, so most users never see it; inheriting from `UserWarning` makes the deprecation visible by default. Pass `category=MCPDeprecationWarning` to every `@deprecated` decorator, export the class from `mcp`, and document how to silence it.
1 parent 86bd4f3 commit a5c1522

12 files changed

Lines changed: 52 additions & 25 deletions

File tree

docs/migration.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,15 @@ The user-facing methods for these features now carry `typing_extensions.deprecat
12261226
- Roots: `ServerSession.list_roots()`, `ClientPeer.list_roots()`, `ClientSession.send_roots_list_changed()`, `Client.send_roots_list_changed()`
12271227
- Logging: `ServerSession.send_log_message()`, `ClientSession.set_logging_level()`, `Client.set_logging_level()`, and the `MCPServer` `Context` helpers `log()`, `debug()`, `info()`, `warning()`, `error()`
12281228

1229+
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:
1230+
1231+
```python
1232+
import warnings
1233+
from mcp import MCPDeprecationWarning
1234+
1235+
warnings.filterwarnings("ignore", category=MCPDeprecationWarning)
1236+
```
1237+
12291238
No migration is required during the deprecation window. New code should avoid building on these features, since they may be removed in a future spec version.
12301239

12311240
## Bug Fixes

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ filterwarnings = [
216216
# SEP-2577 deprecates the roots/sampling/logging methods; the SDK still calls
217217
# them internally (e.g. `ctx.debug` -> `log` -> `send_log_message`), so the
218218
# advisory warning is silenced. Tests asserting it opt back in with pytest.warns.
219-
"ignore:`.*` is deprecated as of 2026-07-28 \\(SEP-2577\\).:DeprecationWarning",
219+
"ignore:`.*` is deprecated as of 2026-07-28 \\(SEP-2577\\).:mcp.MCPDeprecationWarning",
220220
]
221221

222222
[tool.markdown.lint]

src/mcp/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .client.stdio import StdioServerParameters, stdio_client
55
from .server.session import ServerSession
66
from .server.stdio import stdio_server
7-
from .shared.exceptions import MCPError, UrlElicitationRequiredError
7+
from .shared.exceptions import MCPDeprecationWarning, MCPError, UrlElicitationRequiredError
88
from .types import (
99
CallToolRequest,
1010
ClientCapabilities,
@@ -96,6 +96,7 @@
9696
"ListToolsResult",
9797
"LoggingLevel",
9898
"LoggingMessageNotification",
99+
"MCPDeprecationWarning",
99100
"MCPError",
100101
"Notification",
101102
"PingRequest",

src/mcp/client/client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from mcp.server import Server
1616
from mcp.server.mcpserver import MCPServer
1717
from mcp.shared.dispatcher import ProgressFnT
18+
from mcp.shared.exceptions import MCPDeprecationWarning
1819
from mcp.types import (
1920
CallToolResult,
2021
CompleteResult,
@@ -197,7 +198,7 @@ async def send_progress_notification(
197198
message=message,
198199
)
199200

200-
@deprecated("`set_logging_level` is deprecated as of 2026-07-28 (SEP-2577).")
201+
@deprecated("`set_logging_level` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
201202
async def set_logging_level(self, level: LoggingLevel, *, meta: RequestParamsMeta | None = None) -> EmptyResult:
202203
"""Set the logging level on the server."""
203204
return await self.session.set_logging_level(level=level, meta=meta) # pyright: ignore[reportDeprecated]
@@ -315,7 +316,7 @@ async def list_tools(self, *, cursor: str | None = None, meta: RequestParamsMeta
315316
"""List available tools from the server."""
316317
return await self.session.list_tools(params=PaginatedRequestParams(cursor=cursor, _meta=meta))
317318

318-
@deprecated("`send_roots_list_changed` is deprecated as of 2026-07-28 (SEP-2577).")
319+
@deprecated("`send_roots_list_changed` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
319320
async def send_roots_list_changed(self) -> None:
320321
"""Send a notification that the roots list has changed."""
321322
# TODO(Marcelo): Currently, there is no way for the server to handle this. We should add support.

src/mcp/client/session.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from mcp.client._transport import ReadStream, WriteStream
1717
from mcp.shared._compat import resync_tracer
1818
from mcp.shared.dispatcher import CallOptions, DispatchContext, Dispatcher, ProgressFnT
19-
from mcp.shared.exceptions import MCPError
19+
from mcp.shared.exceptions import MCPDeprecationWarning, MCPError
2020
from mcp.shared.jsonrpc_dispatcher import JSONRPCDispatcher
2121
from mcp.shared.message import ClientMessageMetadata, SessionMessage
2222
from mcp.shared.session import RequestResponder
@@ -386,7 +386,7 @@ async def send_progress_notification(
386386
)
387387
)
388388

389-
@deprecated("`set_logging_level` is deprecated as of 2026-07-28 (SEP-2577).")
389+
@deprecated("`set_logging_level` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
390390
async def set_logging_level(
391391
self,
392392
level: types.LoggingLevel,
@@ -551,7 +551,7 @@ async def list_tools(self, *, params: types.PaginatedRequestParams | None = None
551551

552552
return result
553553

554-
@deprecated("`send_roots_list_changed` is deprecated as of 2026-07-28 (SEP-2577).")
554+
@deprecated("`send_roots_list_changed` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
555555
async def send_roots_list_changed(self) -> None:
556556
"""Send a roots/list_changed notification."""
557557
await self.send_notification(types.RootsListChangedNotification())

src/mcp/server/context.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from mcp.server.session import ServerSession
1010
from mcp.shared.context import BaseContext
1111
from mcp.shared.dispatcher import DispatchContext
12+
from mcp.shared.exceptions import MCPDeprecationWarning
1213
from mcp.shared.message import CloseSSEStreamCallback
1314
from mcp.shared.peer import Meta
1415
from mcp.shared.transport_context import TransportContext
@@ -92,7 +93,7 @@ def headers(self) -> Mapping[str, str] | None:
9293
"""
9394
return self.transport.headers
9495

95-
@deprecated("`log` is deprecated as of 2026-07-28 (SEP-2577).")
96+
@deprecated("`log` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
9697
async def log(self, level: LoggingLevel, data: Any, logger: str | None = None, *, meta: Meta | None = None) -> None:
9798
"""Send a request-scoped `notifications/message` log entry.
9899

src/mcp/server/mcpserver/context.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
elicit_with_validation,
1616
)
1717
from mcp.server.lowlevel.helper_types import ReadResourceContents
18+
from mcp.shared.exceptions import MCPDeprecationWarning
1819
from mcp.types import LoggingLevel
1920

2021
if TYPE_CHECKING:
@@ -190,7 +191,7 @@ async def elicit_url(
190191
related_request_id=self.request_id,
191192
)
192193

193-
@deprecated("`log` is deprecated as of 2026-07-28 (SEP-2577).")
194+
@deprecated("`log` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
194195
async def log(
195196
self,
196197
level: LoggingLevel,
@@ -267,22 +268,22 @@ async def close_standalone_sse_stream(self) -> None:
267268
await self._request_context.close_standalone_sse_stream()
268269

269270
# Convenience methods for common log levels
270-
@deprecated("`debug` is deprecated as of 2026-07-28 (SEP-2577).")
271+
@deprecated("`debug` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
271272
async def debug(self, data: Any, *, logger_name: str | None = None) -> None:
272273
"""Send a debug log message."""
273274
await self.log("debug", data, logger_name=logger_name) # pyright: ignore[reportDeprecated]
274275

275-
@deprecated("`info` is deprecated as of 2026-07-28 (SEP-2577).")
276+
@deprecated("`info` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
276277
async def info(self, data: Any, *, logger_name: str | None = None) -> None:
277278
"""Send an info log message."""
278279
await self.log("info", data, logger_name=logger_name) # pyright: ignore[reportDeprecated]
279280

280-
@deprecated("`warning` is deprecated as of 2026-07-28 (SEP-2577).")
281+
@deprecated("`warning` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
281282
async def warning(self, data: Any, *, logger_name: str | None = None) -> None:
282283
"""Send a warning log message."""
283284
await self.log("warning", data, logger_name=logger_name) # pyright: ignore[reportDeprecated]
284285

285-
@deprecated("`error` is deprecated as of 2026-07-28 (SEP-2577).")
286+
@deprecated("`error` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
286287
async def error(self, data: Any, *, logger_name: str | None = None) -> None:
287288
"""Send an error log message."""
288289
await self.log("error", data, logger_name=logger_name) # pyright: ignore[reportDeprecated]

src/mcp/server/session.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from mcp.server.connection import Connection
1919
from mcp.server.validation import validate_sampling_tools, validate_tool_use_result_messages
2020
from mcp.shared.dispatcher import CallOptions, Dispatcher, ProgressFnT
21-
from mcp.shared.exceptions import NoBackChannelError, StatelessModeNotSupported
21+
from mcp.shared.exceptions import MCPDeprecationWarning, NoBackChannelError, StatelessModeNotSupported
2222
from mcp.shared.message import ServerMessageMetadata
2323
from mcp.types import methods as _methods
2424

@@ -127,7 +127,7 @@ def check_client_capability(self, capability: types.ClientCapabilities) -> bool:
127127
"""Check if the client supports a specific capability."""
128128
return self._connection.check_capability(capability)
129129

130-
@deprecated("`send_log_message` is deprecated as of 2026-07-28 (SEP-2577).")
130+
@deprecated("`send_log_message` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
131131
async def send_log_message(
132132
self,
133133
level: types.LoggingLevel,
@@ -156,7 +156,7 @@ async def send_resource_updated(self, uri: str | AnyUrl) -> None:
156156
)
157157

158158
@overload
159-
@deprecated("`create_message` is deprecated as of 2026-07-28 (SEP-2577).")
159+
@deprecated("`create_message` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
160160
async def create_message(
161161
self,
162162
messages: list[types.SamplingMessage],
@@ -176,7 +176,7 @@ async def create_message(
176176
...
177177

178178
@overload
179-
@deprecated("`create_message` is deprecated as of 2026-07-28 (SEP-2577).")
179+
@deprecated("`create_message` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
180180
async def create_message(
181181
self,
182182
messages: list[types.SamplingMessage],
@@ -195,7 +195,7 @@ async def create_message(
195195
"""Overload: With tools, returns array-capable content."""
196196
...
197197

198-
@deprecated("`create_message` is deprecated as of 2026-07-28 (SEP-2577).")
198+
@deprecated("`create_message` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
199199
async def create_message(
200200
self,
201201
messages: list[types.SamplingMessage],
@@ -272,7 +272,7 @@ async def create_message(
272272
metadata=metadata_obj,
273273
)
274274

275-
@deprecated("`list_roots` is deprecated as of 2026-07-28 (SEP-2577).")
275+
@deprecated("`list_roots` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
276276
async def list_roots(self) -> types.ListRootsResult:
277277
"""Send a roots/list request."""
278278
if self._stateless:

src/mcp/shared/exceptions.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@
55
from mcp.types import INVALID_REQUEST, URL_ELICITATION_REQUIRED, ElicitRequestURLParams, ErrorData, JSONRPCError
66

77

8+
class MCPDeprecationWarning(UserWarning):
9+
"""A custom deprecation warning for the MCP SDK.
10+
11+
Unlike the built-in `DeprecationWarning`, this inherits from `UserWarning` so
12+
it is shown by default, helping users discover deprecated features without
13+
enabling warnings explicitly.
14+
15+
Reference: https://sethmlarson.dev/deprecations-via-warnings-dont-work-for-python-libraries
16+
"""
17+
18+
819
class MCPError(Exception):
920
"""Exception type raised when an error arrives over an MCP connection."""
1021

src/mcp/shared/peer.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from typing_extensions import deprecated
1717

1818
from mcp.shared.dispatcher import CallOptions, Outbound
19+
from mcp.shared.exceptions import MCPDeprecationWarning
1920
from mcp.types import (
2021
CreateMessageRequestParams,
2122
CreateMessageResult,
@@ -84,7 +85,7 @@ async def notify(self, method: str, params: Mapping[str, Any] | None) -> None:
8485
await self._outbound.notify(method, params)
8586

8687
@overload
87-
@deprecated("`sample` is deprecated as of 2026-07-28 (SEP-2577).")
88+
@deprecated("`sample` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
8889
async def sample(
8990
self,
9091
messages: list[SamplingMessage],
@@ -102,7 +103,7 @@ async def sample(
102103
opts: CallOptions | None = None,
103104
) -> CreateMessageResult: ...
104105
@overload
105-
@deprecated("`sample` is deprecated as of 2026-07-28 (SEP-2577).")
106+
@deprecated("`sample` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
106107
async def sample(
107108
self,
108109
messages: list[SamplingMessage],
@@ -119,7 +120,7 @@ async def sample(
119120
meta: Meta | None = None,
120121
opts: CallOptions | None = None,
121122
) -> CreateMessageResultWithTools: ...
122-
@deprecated("`sample` is deprecated as of 2026-07-28 (SEP-2577).")
123+
@deprecated("`sample` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
123124
async def sample(
124125
self,
125126
messages: list[SamplingMessage],
@@ -199,7 +200,7 @@ async def elicit_url(
199200
result = await self.send_raw_request("elicitation/create", dump_params(params, meta), opts)
200201
return ElicitResult.model_validate(result, by_name=False)
201202

202-
@deprecated("`list_roots` is deprecated as of 2026-07-28 (SEP-2577).")
203+
@deprecated("`list_roots` is deprecated as of 2026-07-28 (SEP-2577).", category=MCPDeprecationWarning)
203204
async def list_roots(self, *, meta: Meta | None = None, opts: CallOptions | None = None) -> ListRootsResult:
204205
"""Send a `roots/list` request.
205206

0 commit comments

Comments
 (0)