Deprecate roots, sampling, and logging methods per SEP-2577 (#2926)

This commit is contained in:
Marcelo Trylesinski 2026-06-20 17:25:41 +01:00 • committed by GitHub
commit 4573e4ac33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 206 additions and 123 deletions

View file

@ -156,7 +156,7 @@ async def test_a_tool_rejected_with_url_elicitation_required_succeeds_on_retry_a
if not authorised[0]:
# The log line gives the message handler a non-completion notification, so the test's
# filtering branch is exercised in both directions and the wait remains specific.
await ctx.session.send_log_message(level="warning", data="authorisation required", logger="gate")
await ctx.session.send_log_message(level="warning", data="authorisation required", logger="gate") # pyright: ignore[reportDeprecated]
raise UrlElicitationRequiredError(
[
ElicitRequestURLParams(

View file

@ -39,7 +39,7 @@ async def test_set_logging_level_reaches_handler(connect: Connect) -> None:
server = Server("logger", on_set_logging_level=set_logging_level)
async with connect(server) as client:
result = await client.set_logging_level("warning")
result = await client.set_logging_level("warning") # pyright: ignore[reportDeprecated]
assert result == snapshot(EmptyResult())
@ -64,10 +64,10 @@ async def test_log_messages_reach_logging_callback_in_order(connect: Connect) ->
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "chatty"
await ctx.session.send_log_message(
await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
level="info", data="starting up", logger="app.lifecycle", related_request_id=ctx.request_id
)
await ctx.session.send_log_message(
await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
level="error", data={"code": 502, "retryable": True}, related_request_id=ctx.request_id
)
return CallToolResult(content=[TextContent(text="done")])
@ -106,7 +106,7 @@ async def test_log_messages_at_every_severity_level(connect: Connect) -> None:
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "siren"
for level in ALL_LEVELS:
await ctx.session.send_log_message(
await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
level=level, data=f"a {level} message", related_request_id=ctx.request_id
)
return CallToolResult(content=[TextContent(text="logged")])

View file

@ -29,7 +29,7 @@ async def test_list_roots_round_trip(connect: Connect) -> None:
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "show_roots"
result = await ctx.session.list_roots()
result = await ctx.session.list_roots() # pyright: ignore[reportDeprecated]
lines = [f"{root.uri} name={root.name}" for root in result.roots]
return CallToolResult(content=[TextContent(text="\n".join(lines))])
@ -64,7 +64,7 @@ async def test_list_roots_empty(connect: Connect) -> None:
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "count_roots"
result = await ctx.session.list_roots()
result = await ctx.session.list_roots() # pyright: ignore[reportDeprecated]
return CallToolResult(content=[TextContent(text=str(len(result.roots)))])
server = Server("rooted", on_list_tools=list_tools, on_call_tool=call_tool)
@ -94,7 +94,7 @@ async def test_list_roots_without_callback_is_error(connect: Connect) -> None:
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "show_roots"
try:
await ctx.session.list_roots()
await ctx.session.list_roots() # pyright: ignore[reportDeprecated]
except MCPError as exc:
return CallToolResult(content=[TextContent(text=f"{exc.error.code}: {exc.error.message}")])
raise NotImplementedError # list_roots cannot succeed without a client callback
@ -122,7 +122,7 @@ async def test_list_roots_callback_error_surfaces_to_the_handler(connect: Connec
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "show_roots"
try:
await ctx.session.list_roots()
await ctx.session.list_roots() # pyright: ignore[reportDeprecated]
except MCPError as exc:
return CallToolResult(content=[TextContent(text=f"{exc.error.code}: {exc.error.message}")])
raise NotImplementedError # the callback always answers with an error
@ -159,7 +159,7 @@ async def test_roots_list_changed_reaches_server_handler(connect: Connect) -> No
raise NotImplementedError
async with connect(server, list_roots_callback=list_roots) as client:
await client.send_roots_list_changed()
await client.send_roots_list_changed() # pyright: ignore[reportDeprecated]
with anyio.fail_after(5):
await delivered.wait()

View file

@ -49,7 +49,7 @@ async def test_create_message_round_trip(connect: Connect) -> None:
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "ask_model"
result = await ctx.session.create_message(
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=TextContent(text="Say hello."))],
max_tokens=100,
)
@ -104,7 +104,7 @@ async def test_create_message_params_reach_callback(connect: Connect) -> None:
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "ask_model"
result = await ctx.session.create_message(
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=TextContent(text="Pick a model."))],
max_tokens=50,
system_prompt="You are terse.",
@ -170,7 +170,7 @@ async def test_create_message_request_with_image_content_reaches_callback(connec
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "describe_image"
result = await ctx.session.create_message(
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=ImageContent(data="aW1n", mime_type="image/png"))],
max_tokens=100,
)
@ -220,7 +220,7 @@ async def test_create_message_result_with_image_content_returns_to_handler(conne
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "draw"
result = await ctx.session.create_message(
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=TextContent(text="Draw a cat."))],
max_tokens=100,
)
@ -261,7 +261,7 @@ async def test_create_message_callback_error(connect: Connect) -> None:
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "ask_model"
try:
await ctx.session.create_message(
await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=TextContent(text="Say hello."))],
max_tokens=100,
)
@ -292,7 +292,7 @@ async def test_create_message_without_callback_is_error(connect: Connect) -> Non
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "ask_model"
try:
await ctx.session.create_message(
await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=TextContent(text="Say hello."))],
max_tokens=100,
)
@ -324,7 +324,7 @@ async def test_create_message_with_tools_is_rejected_for_unsupporting_client(con
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "ask_model"
try:
await ctx.session.create_message(
await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=TextContent(text="What is the weather?"))],
max_tokens=100,
tools=[types.Tool(name="get_weather", input_schema={"type": "object"})],
@ -366,7 +366,7 @@ async def test_create_message_with_mixed_tool_result_content_is_rejected(connect
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "summarise_tools"
try:
await ctx.session.create_message(
await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[
SamplingMessage(
role="user",
@ -452,7 +452,7 @@ async def test_create_message_request_with_audio_content_reaches_callback(connec
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "transcribe"
result = await ctx.session.create_message(
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=AudioContent(data="c25k", mime_type="audio/wav"))],
max_tokens=100,
)
@ -502,7 +502,7 @@ async def test_create_message_result_with_audio_content_returns_to_handler(conne
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "speak"
result = await ctx.session.create_message(
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=TextContent(text="Say hello, aloud."))],
max_tokens=100,
)
@ -539,7 +539,7 @@ async def test_create_message_with_list_valued_message_content_reaches_callback(
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "caption"
result = await ctx.session.create_message(
result = await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[
SamplingMessage(
role="user",
@ -606,7 +606,7 @@ async def test_create_message_with_mismatched_tool_use_and_result_ids_is_rejecte
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "continue_tools"
try:
await ctx.session.create_message(
await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[
SamplingMessage(
role="assistant",
@ -662,7 +662,7 @@ async def test_array_content_result_for_a_tool_free_request_surfaces_as_a_valida
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "ask_model"
try:
await ctx.session.create_message(
await ctx.session.create_message( # pyright: ignore[reportDeprecated]
messages=[SamplingMessage(role="user", content=TextContent(text="Two thoughts, please."))],
max_tokens=100,
)

View file

@ -96,7 +96,7 @@ async def test_notifications_are_never_answered() -> None:
recording = RecordingTransport(InMemoryTransport(_echo_server()))
async with Client(recording, list_roots_callback=list_roots) as client:
await client.send_roots_list_changed()
await client.send_roots_list_changed() # pyright: ignore[reportDeprecated]
await client.send_ping()
sent = [message.message for message in recording.sent]

View file

@ -41,10 +41,10 @@ async def test_context_logging_helpers_send_log_notifications(connect: Connect)
@mcp.tool()
async def narrate(ctx: Context) -> str:
await ctx.debug("d")
await ctx.info("i")
await ctx.warning("w")
await ctx.error("e")
await ctx.debug("d") # pyright: ignore[reportDeprecated]
await ctx.info("i") # pyright: ignore[reportDeprecated]
await ctx.warning("w") # pyright: ignore[reportDeprecated]
await ctx.error("e") # pyright: ignore[reportDeprecated]
return "done"
async def collect(params: LoggingMessageNotificationParams) -> None:
@ -136,7 +136,7 @@ async def test_report_progress_without_a_progress_token_sends_nothing(connect: C
@mcp.tool()
async def mill(ctx: Context) -> str:
await ctx.report_progress(1, 3)
await ctx.info("milling done")
await ctx.info("milling done") # pyright: ignore[reportDeprecated]
return "milled"
async def collect(message: IncomingMessage) -> None:
@ -250,8 +250,8 @@ async def test_set_logging_level_is_rejected_and_messages_are_never_filtered(con
@mcp.tool()
async def chatter(ctx: Context) -> str:
await ctx.debug("noise")
await ctx.error("signal")
await ctx.debug("noise") # pyright: ignore[reportDeprecated]
await ctx.error("signal") # pyright: ignore[reportDeprecated]
return "done"
async def collect(params: LoggingMessageNotificationParams) -> None:
@ -259,7 +259,7 @@ async def test_set_logging_level_is_rejected_and_messages_are_never_filtered(con
async with connect(mcp, logging_callback=collect) as client:
with pytest.raises(MCPError) as exc_info:
await client.set_logging_level("error")
await client.set_logging_level("error") # pyright: ignore[reportDeprecated]
await client.call_tool("chatter", {})

View file

@ -414,7 +414,7 @@ async def test_adding_and_removing_tools_does_not_notify_connected_clients(conne
async def grow(ctx: Context) -> str:
mcp.add_tool(extra, name="extra")
mcp.remove_tool("doomed")
await ctx.info("tool set changed")
await ctx.info("tool set changed") # pyright: ignore[reportDeprecated]
return "mutated"
async def collect(message: IncomingMessage) -> None:

View file

@ -7,12 +7,14 @@ test-only-functions convention.
"""
import sys
import warnings
import anyio
import coverage
from mcp.server import Server, ServerRequestContext
from mcp.server.stdio import stdio_server
from mcp.shared.exceptions import MCPDeprecationWarning
from mcp.types import (
CallToolRequestParams,
CallToolResult,
@ -40,7 +42,9 @@ async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) ->
assert params.name == "echo"
assert params.arguments is not None
text = params.arguments["text"]
await ctx.session.send_log_message(level="info", data=f"echoing {text}", logger="echo")
with warnings.catch_warnings():
warnings.simplefilter("ignore", MCPDeprecationWarning)
await ctx.session.send_log_message(level="info", data=f"echoing {text}", logger="echo") # pyright: ignore[reportDeprecated]
return CallToolResult(content=[TextContent(text=text)])

View file

@ -32,7 +32,7 @@ async def test_concurrent_clients_on_one_stateful_server_receive_only_their_own_
@mcp.tool()
async def announce(label: str, ctx: Context) -> str:
"""Emit one info-level log carrying the caller's label, then return it."""
await ctx.info(label)
await ctx.info(label) # pyright: ignore[reportDeprecated]
return label
received_a: list[object] = []

View file

@ -52,7 +52,7 @@ def _server() -> Server:
async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult:
assert params.name == "narrate"
await ctx.session.send_log_message(level="info", data="related", logger=None, related_request_id=ctx.request_id)
await ctx.session.send_log_message(level="info", data="related", logger=None, related_request_id=ctx.request_id) # pyright: ignore[reportDeprecated]
await ctx.session.send_resource_updated("file:///watched.txt")
return CallToolResult(content=[TextContent(text="done")])

View file

@ -55,7 +55,7 @@ def _counting_server() -> MCPServer:
async def count(ctx: Context, n: int) -> str:
"""Emit n log notifications related to this call, plus one unrelated resource update."""
for i in range(1, n + 1):
await ctx.info(f"tick {i}")
await ctx.info(f"tick {i}") # pyright: ignore[reportDeprecated]
await ctx.session.send_resource_updated("file:///elsewhere.txt")
return f"counted to {n}"
@ -138,10 +138,10 @@ async def test_get_with_last_event_id_replays_only_that_streams_missed_events()
@mcp.tool()
async def count(ctx: Context) -> str:
"""Emit one related notification, wait for the test, then emit two more plus an unrelated one."""
await ctx.info("tick 1")
await ctx.info("tick 1") # pyright: ignore[reportDeprecated]
await release.wait()
await ctx.info("tick 2")
await ctx.info("tick 3")
await ctx.info("tick 2") # pyright: ignore[reportDeprecated]
await ctx.info("tick 3") # pyright: ignore[reportDeprecated]
await ctx.session.send_resource_updated("file:///elsewhere.txt")
return "counted"
@ -219,7 +219,7 @@ async def test_dropping_the_connection_mid_request_does_not_cancel_the_handler()
"""Signal start, wait for the test, signal completion."""
started.set()
await release.wait()
await ctx.info("released")
await ctx.info("released") # pyright: ignore[reportDeprecated]
finished.set()
return "held"
@ -263,10 +263,10 @@ async def test_a_call_whose_stream_the_server_closes_is_resumed_by_the_client()
@mcp.tool()
async def interrupt(ctx: Context) -> str:
"""Emit, close this call's SSE stream, then emit again after the test releases the gate."""
await ctx.info("before close")
await ctx.info("before close") # pyright: ignore[reportDeprecated]
await ctx.close_sse_stream()
await gate.wait()
await ctx.info("after close")
await ctx.info("after close") # pyright: ignore[reportDeprecated]
done.set()
return "resumed"
@ -321,9 +321,9 @@ async def test_a_captured_resumption_token_replays_missed_messages_on_a_new_conn
@mcp.tool()
async def hold(ctx: Context) -> str:
"""Emit one notification, wait for the test, emit another, return."""
await ctx.info("first")
await ctx.info("first") # pyright: ignore[reportDeprecated]
await release.wait()
await ctx.info("second")
await ctx.info("second") # pyright: ignore[reportDeprecated]
return "done"
async def on_token(token: str) -> None:

View file

@ -55,7 +55,7 @@ def _smoke_server() -> MCPServer:
@mcp.tool()
async def announce(ctx: Context) -> str:
"""Send one notification related to this request and one that is not."""
await ctx.info("about to announce")
await ctx.info("about to announce") # pyright: ignore[reportDeprecated]
await ctx.session.send_resource_updated("file:///watched.txt")
return "announced"