Skip to content

Commit 4573e4a

Browse files
authored
Deprecate roots, sampling, and logging methods per SEP-2577 (#2926)
1 parent 5734acf commit 4573e4a

44 files changed

Lines changed: 206 additions & 123 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.v2.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ mcp = MCPServer(name="Progress Example")
360360
@mcp.tool()
361361
async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str:
362362
"""Execute a task with progress updates."""
363-
await ctx.info(f"Starting: {task_name}")
363+
await ctx.info(f"Starting: {task_name}") # pyright: ignore[reportDeprecated]
364364

365365
for i in range(steps):
366366
progress = (i + 1) / steps
@@ -369,7 +369,7 @@ async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str
369369
total=1.0,
370370
message=f"Step {i + 1}/{steps}",
371371
)
372-
await ctx.debug(f"Completed step {i + 1}")
372+
await ctx.debug(f"Completed step {i + 1}") # pyright: ignore[reportDeprecated]
373373

374374
return f"Task '{task_name}' completed"
375375
```
@@ -707,7 +707,7 @@ mcp = MCPServer(name="Progress Example")
707707
@mcp.tool()
708708
async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str:
709709
"""Execute a task with progress updates."""
710-
await ctx.info(f"Starting: {task_name}")
710+
await ctx.info(f"Starting: {task_name}") # pyright: ignore[reportDeprecated]
711711

712712
for i in range(steps):
713713
progress = (i + 1) / steps
@@ -716,7 +716,7 @@ async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str
716716
total=1.0,
717717
message=f"Step {i + 1}/{steps}",
718718
)
719-
await ctx.debug(f"Completed step {i + 1}")
719+
await ctx.debug(f"Completed step {i + 1}") # pyright: ignore[reportDeprecated]
720720

721721
return f"Task '{task_name}' completed"
722722
```
@@ -948,7 +948,7 @@ async def generate_poem(topic: str, ctx: Context) -> str:
948948
"""Generate a poem using LLM sampling."""
949949
prompt = f"Write a short poem about {topic}"
950950

951-
result = await ctx.session.create_message(
951+
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
952952
messages=[
953953
SamplingMessage(
954954
role="user",
@@ -982,10 +982,10 @@ mcp = MCPServer(name="Notifications Example")
982982
async def process_data(data: str, ctx: Context) -> str:
983983
"""Process data with logging."""
984984
# Different log levels
985-
await ctx.debug(f"Debug: Processing '{data}'")
986-
await ctx.info("Info: Starting processing")
987-
await ctx.warning("Warning: This is experimental")
988-
await ctx.error("Error: (This is just a demo)")
985+
await ctx.debug(f"Debug: Processing '{data}'") # pyright: ignore[reportDeprecated]
986+
await ctx.info("Info: Starting processing") # pyright: ignore[reportDeprecated]
987+
await ctx.warning("Warning: This is experimental") # pyright: ignore[reportDeprecated]
988+
await ctx.error("Error: (This is just a demo)") # pyright: ignore[reportDeprecated]
989989

990990
# Notify about resource changes
991991
await ctx.session.send_resource_list_changed()

docs/migration.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1245,7 +1245,26 @@ Tasks are expected to return as a separate MCP extension in a future release.
12451245

12461246
## Deprecations
12471247

1248-
<!-- Add deprecations below -->
1248+
### Roots, Sampling, and Logging methods deprecated (SEP-2577)
1249+
1250+
[SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577) deprecates the Roots, Sampling, and Logging features as of the 2026-07-28 spec. The deprecation is advisory only: there are no wire-level changes, capability negotiation is unchanged, and every method keeps working for sessions negotiating 2025-11-25 and earlier.
1251+
1252+
The user-facing methods for these features now carry `typing_extensions.deprecated`, so type checkers, IDEs, and the runtime surface a deprecation warning where they are called:
1253+
1254+
- Sampling: `ServerSession.create_message()`, `ClientPeer.sample()`
1255+
- Roots: `ServerSession.list_roots()`, `ClientPeer.list_roots()`, `ClientSession.send_roots_list_changed()`, `Client.send_roots_list_changed()`
1256+
- 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()`
1257+
1258+
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:
1259+
1260+
```python
1261+
import warnings
1262+
from mcp import MCPDeprecationWarning
1263+
1264+
warnings.filterwarnings("ignore", category=MCPDeprecationWarning)
1265+
```
1266+
1267+
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.
12491268

12501269
## Bug Fixes
12511270

examples/servers/everything-server/mcp_everything_server/server.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,13 @@ def test_multiple_content_types() -> list[TextContent | ImageContent | EmbeddedR
143143
@mcp.tool()
144144
async def test_tool_with_logging(ctx: Context) -> str:
145145
"""Tests tool that emits log messages during execution"""
146-
await ctx.info("Tool execution started")
146+
await ctx.info("Tool execution started") # pyright: ignore[reportDeprecated]
147147
await asyncio.sleep(0.05)
148148

149-
await ctx.info("Tool processing data")
149+
await ctx.info("Tool processing data") # pyright: ignore[reportDeprecated]
150150
await asyncio.sleep(0.05)
151151

152-
await ctx.info("Tool execution completed")
152+
await ctx.info("Tool execution completed") # pyright: ignore[reportDeprecated]
153153
return "Tool with logging executed successfully"
154154

155155

@@ -176,7 +176,7 @@ async def test_sampling(prompt: str, ctx: Context) -> str:
176176
"""Tests server-initiated sampling (LLM completion request)"""
177177
try:
178178
# Request sampling from client
179-
result = await ctx.session.create_message(
179+
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
180180
messages=[SamplingMessage(role="user", content=TextContent(type="text", text=prompt))],
181181
max_tokens=100,
182182
)
@@ -314,13 +314,13 @@ def test_error_handling() -> str:
314314
@mcp.tool()
315315
async def test_reconnection(ctx: Context) -> str:
316316
"""Tests SSE polling by closing stream mid-call (SEP-1699)"""
317-
await ctx.info("Before disconnect")
317+
await ctx.info("Before disconnect") # pyright: ignore[reportDeprecated]
318318

319319
await ctx.close_sse_stream()
320320

321321
await asyncio.sleep(0.2) # Wait for client to reconnect
322322

323-
await ctx.info("After reconnect")
323+
await ctx.info("After reconnect") # pyright: ignore[reportDeprecated]
324324
return "Reconnection test completed"
325325

326326

examples/servers/simple-streamablehttp-stateless/mcp_simple_streamablehttp_stateless/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ async def handle_call_tool(ctx: ServerRequestContext, params: types.CallToolRequ
4949

5050
# Send the specified number of notifications with the given interval
5151
for i in range(count):
52-
await ctx.session.send_log_message(
52+
await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
5353
level="info",
5454
data=f"Notification {i + 1}/{count} from caller: {caller}",
5555
logger="notification_stream",

examples/servers/simple-streamablehttp/mcp_simple_streamablehttp/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async def handle_call_tool(ctx: ServerRequestContext, params: types.CallToolRequ
5454
for i in range(count):
5555
# Include more detailed message for resumability demonstration
5656
notification_msg = f"[{i + 1}/{count}] Event from '{caller}' - Use Last-Event-ID to resume if disconnected"
57-
await ctx.session.send_log_message(
57+
await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
5858
level="info",
5959
data=notification_msg,
6060
logger="notification_stream",

examples/servers/sse-polling-demo/mcp_sse_polling_demo/server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ async def handle_call_tool(ctx: ServerRequestContext, params: types.CallToolRequ
7474
content=[types.TextContent(type="text", text="Error: checkpoint_every must be between 1 and 20")]
7575
)
7676

77-
await ctx.session.send_log_message(
77+
await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
7878
level="info",
7979
data=f"Starting batch processing of {items} items...",
8080
logger="process_batch",
@@ -86,7 +86,7 @@ async def handle_call_tool(ctx: ServerRequestContext, params: types.CallToolRequ
8686
await anyio.sleep(0.5)
8787

8888
# Report progress
89-
await ctx.session.send_log_message(
89+
await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
9090
level="info",
9191
data=f"[{i}/{items}] Processing item {i}",
9292
logger="process_batch",
@@ -95,7 +95,7 @@ async def handle_call_tool(ctx: ServerRequestContext, params: types.CallToolRequ
9595

9696
# Checkpoint: close stream to trigger client reconnect
9797
if i % checkpoint_every == 0 and i < items:
98-
await ctx.session.send_log_message(
98+
await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
9999
level="info",
100100
data=f"Checkpoint at item {i} - closing SSE stream for polling",
101101
logger="process_batch",

examples/snippets/servers/notifications.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
async def process_data(data: str, ctx: Context) -> str:
88
"""Process data with logging."""
99
# Different log levels
10-
await ctx.debug(f"Debug: Processing '{data}'")
11-
await ctx.info("Info: Starting processing")
12-
await ctx.warning("Warning: This is experimental")
13-
await ctx.error("Error: (This is just a demo)")
10+
await ctx.debug(f"Debug: Processing '{data}'") # pyright: ignore[reportDeprecated]
11+
await ctx.info("Info: Starting processing") # pyright: ignore[reportDeprecated]
12+
await ctx.warning("Warning: This is experimental") # pyright: ignore[reportDeprecated]
13+
await ctx.error("Error: (This is just a demo)") # pyright: ignore[reportDeprecated]
1414

1515
# Notify about resource changes
1616
await ctx.session.send_resource_list_changed()

examples/snippets/servers/sampling.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ async def generate_poem(topic: str, ctx: Context) -> str:
99
"""Generate a poem using LLM sampling."""
1010
prompt = f"Write a short poem about {topic}"
1111

12-
result = await ctx.session.create_message(
12+
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
1313
messages=[
1414
SamplingMessage(
1515
role="user",

examples/snippets/servers/tool_progress.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
@mcp.tool()
77
async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str:
88
"""Execute a task with progress updates."""
9-
await ctx.info(f"Starting: {task_name}")
9+
await ctx.info(f"Starting: {task_name}") # pyright: ignore[reportDeprecated]
1010

1111
for i in range(steps):
1212
progress = (i + 1) / steps
@@ -15,6 +15,6 @@ async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str
1515
total=1.0,
1616
message=f"Step {i + 1}/{steps}",
1717
)
18-
await ctx.debug(f"Completed step {i + 1}")
18+
await ctx.debug(f"Completed step {i + 1}") # pyright: ignore[reportDeprecated]
1919

2020
return f"Task '{task_name}' completed"

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ filterwarnings = [
213213
"error",
214214
# pywin32 internal deprecation warning
215215
"ignore:getargs.*The 'u' format is deprecated:DeprecationWarning",
216+
# SEP-2577 deprecates the roots/sampling/logging methods; the SDK still calls
217+
# them internally (e.g. `ctx.debug` -> `log` -> `send_log_message`), so the
218+
# advisory warning is silenced. Tests asserting it opt back in with pytest.warns.
219+
"ignore:.*is deprecated as of 2026-07-28 \\(SEP-2577\\).:mcp.MCPDeprecationWarning",
216220
]
217221

218222
[tool.markdown.lint]

0 commit comments

Comments
 (0)