Deprecate roots, sampling, and logging methods per SEP-2577 (#2926)
This commit is contained in:
parent
5734acf18e
commit
4573e4ac33
12 changed files with 206 additions and 123 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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")])
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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", {})
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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] = []
|
||||
|
|
|
|||
|
|
@ -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")])
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue