Completely drop RootModel from types module (#1910)
This commit is contained in:
parent
f4672c5084
commit
5fdd48a0d3
20 changed files with 640 additions and 800 deletions
|
|
@ -19,7 +19,7 @@ from starlette.routing import Route
|
|||
from mcp import ClientSession, types
|
||||
from mcp.client.streamable_http import streamable_http_client
|
||||
from mcp.shared.session import RequestResponder
|
||||
from mcp.types import ClientNotification, RootsListChangedNotification
|
||||
from mcp.types import RootsListChangedNotification
|
||||
from tests.test_helpers import wait_for_server
|
||||
|
||||
|
||||
|
|
@ -135,9 +135,7 @@ async def test_non_compliant_notification_response(non_sdk_server: None, non_sdk
|
|||
await session.initialize()
|
||||
|
||||
# The test server returns a 204 instead of the expected 202
|
||||
await session.send_notification(
|
||||
ClientNotification(RootsListChangedNotification(method="notifications/roots/list_changed"))
|
||||
)
|
||||
await session.send_notification(RootsListChangedNotification(method="notifications/roots/list_changed"))
|
||||
|
||||
if returned_exception: # pragma: no cover
|
||||
pytest.fail(f"Server encountered an exception: {returned_exception}")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from unittest.mock import patch
|
|||
|
||||
import anyio
|
||||
import pytest
|
||||
from pydantic import TypeAdapter
|
||||
|
||||
from mcp.shared.message import SessionMessage
|
||||
from mcp.shared.session import BaseSession, RequestId, SendResultT
|
||||
|
|
@ -23,20 +24,23 @@ async def test_send_request_stream_cleanup():
|
|||
) -> None: # pragma: no cover
|
||||
pass
|
||||
|
||||
@property
|
||||
def _receive_request_adapter(self) -> TypeAdapter[Any]:
|
||||
return TypeAdapter(object) # pragma: no cover
|
||||
|
||||
@property
|
||||
def _receive_notification_adapter(self) -> TypeAdapter[Any]:
|
||||
return TypeAdapter(object) # pragma: no cover
|
||||
|
||||
# Create streams
|
||||
write_stream_send, write_stream_receive = anyio.create_memory_object_stream[SessionMessage](1)
|
||||
read_stream_send, read_stream_receive = anyio.create_memory_object_stream[SessionMessage](1)
|
||||
|
||||
# Create the session
|
||||
session = TestSession(
|
||||
read_stream_receive,
|
||||
write_stream_send,
|
||||
object, # Request type doesn't matter for this test
|
||||
object, # Notification type doesn't matter for this test
|
||||
)
|
||||
session = TestSession(read_stream_receive, write_stream_send)
|
||||
|
||||
# Create a test request
|
||||
request = ClientRequest(PingRequest())
|
||||
request = PingRequest()
|
||||
|
||||
# Patch the _write_stream.send method to raise an exception
|
||||
async def mock_send(*args: Any, **kwargs: Any):
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ from mcp.shared.version import SUPPORTED_PROTOCOL_VERSIONS
|
|||
from mcp.types import (
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
CallToolResult,
|
||||
ClientNotification,
|
||||
ClientRequest,
|
||||
Implementation,
|
||||
InitializedNotification,
|
||||
InitializeRequest,
|
||||
|
|
@ -22,8 +20,9 @@ from mcp.types import (
|
|||
JSONRPCRequest,
|
||||
JSONRPCResponse,
|
||||
ServerCapabilities,
|
||||
ServerResult,
|
||||
TextContent,
|
||||
client_notification_adapter,
|
||||
client_request_adapter,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -41,24 +40,22 @@ async def test_client_session_initialize():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
assert isinstance(request, InitializeRequest)
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(
|
||||
logging=None,
|
||||
resources=None,
|
||||
tools=None,
|
||||
experimental=None,
|
||||
prompts=None,
|
||||
),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
instructions="The server instructions.",
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(
|
||||
logging=None,
|
||||
resources=None,
|
||||
tools=None,
|
||||
experimental=None,
|
||||
prompts=None,
|
||||
),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
instructions="The server instructions.",
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -74,7 +71,7 @@ async def test_client_session_initialize():
|
|||
session_notification = await client_to_server_receive.receive()
|
||||
jsonrpc_notification = session_notification.message
|
||||
assert isinstance(jsonrpc_notification, JSONRPCNotification)
|
||||
initialized_notification = ClientNotification.model_validate(
|
||||
initialized_notification = client_notification_adapter.validate_python(
|
||||
jsonrpc_notification.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
|
||||
|
|
@ -109,7 +106,7 @@ async def test_client_session_initialize():
|
|||
|
||||
# Check that the client sent the initialized notification
|
||||
assert initialized_notification
|
||||
assert isinstance(initialized_notification.root, InitializedNotification)
|
||||
assert isinstance(initialized_notification, InitializedNotification)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -126,18 +123,16 @@ async def test_client_session_custom_client_info():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_client_info = request.root.params.client_info
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_client_info = request.params.client_info
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -185,18 +180,16 @@ async def test_client_session_default_client_info():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_client_info = request.root.params.client_info
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_client_info = request.params.client_info
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -238,21 +231,19 @@ async def test_client_session_version_negotiation_success():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
assert isinstance(request, InitializeRequest)
|
||||
|
||||
# Verify client sent the latest protocol version
|
||||
assert request.root.params.protocol_version == LATEST_PROTOCOL_VERSION
|
||||
assert request.params.protocol_version == LATEST_PROTOCOL_VERSION
|
||||
|
||||
# Server responds with a supported older version
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version="2024-11-05",
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version="2024-11-05",
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -295,18 +286,16 @@ async def test_client_session_version_negotiation_failure():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
assert isinstance(request, InitializeRequest)
|
||||
|
||||
# Server responds with an unsupported version
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version="2020-01-01", # Unsupported old version
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version="2020-01-01", # Unsupported old version
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -349,18 +338,16 @@ async def test_client_capabilities_default():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_capabilities = request.root.params.capabilities
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_capabilities = request.params.capabilities
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -422,18 +409,16 @@ async def test_client_capabilities_with_custom_callbacks():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_capabilities = request.root.params.capabilities
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_capabilities = request.params.capabilities
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -503,18 +488,16 @@ async def test_client_capabilities_with_sampling_tools():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_capabilities = request.root.params.capabilities
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_capabilities = request.params.capabilities
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -572,17 +555,15 @@ async def test_get_server_capabilities():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
assert isinstance(request, InitializeRequest)
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=expected_capabilities,
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=expected_capabilities,
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -639,17 +620,15 @@ async def test_client_tool_call_with_meta(meta: dict[str, Any] | None):
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
assert isinstance(request, InitializeRequest)
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
# Answer initialization request
|
||||
|
|
@ -678,9 +657,7 @@ async def test_client_tool_call_with_meta(meta: dict[str, Any] | None):
|
|||
assert "_meta" in jsonrpc_request.params
|
||||
assert jsonrpc_request.params["_meta"] == meta
|
||||
|
||||
result = ServerResult(
|
||||
CallToolResult(content=[TextContent(type="text", text="Called successfully")], is_error=False)
|
||||
)
|
||||
result = CallToolResult(content=[TextContent(type="text", text="Called successfully")], is_error=False)
|
||||
|
||||
# Send the tools/call result
|
||||
await server_to_client_send.send(
|
||||
|
|
|
|||
|
|
@ -11,14 +11,13 @@ from mcp.shared.context import RequestContext
|
|||
from mcp.shared.message import SessionMessage
|
||||
from mcp.types import (
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
ClientRequest,
|
||||
Implementation,
|
||||
InitializeRequest,
|
||||
InitializeResult,
|
||||
JSONRPCRequest,
|
||||
JSONRPCResponse,
|
||||
ServerCapabilities,
|
||||
ServerResult,
|
||||
client_request_adapter,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -36,18 +35,16 @@ async def test_client_capabilities_without_tasks():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_capabilities = request.root.params.capabilities
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_capabilities = request.params.capabilities
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -108,18 +105,16 @@ async def test_client_capabilities_with_tasks():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_capabilities = request.root.params.capabilities
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_capabilities = request.params.capabilities
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -190,18 +185,16 @@ async def test_client_capabilities_auto_built_from_handlers():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_capabilities = request.root.params.capabilities
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_capabilities = request.params.capabilities
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
@ -268,18 +261,16 @@ async def test_client_capabilities_with_task_augmented_handlers():
|
|||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
request = client_request_adapter.validate_python(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_capabilities = request.root.params.capabilities
|
||||
assert isinstance(request, InitializeRequest)
|
||||
received_capabilities = request.params.capabilities
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
result = InitializeResult(
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
|
||||
async with server_to_client_send:
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ from mcp.types import (
|
|||
CallToolResult,
|
||||
CancelTaskRequest,
|
||||
CancelTaskResult,
|
||||
ClientRequest,
|
||||
ClientResult,
|
||||
CreateTaskResult,
|
||||
GetTaskPayloadRequest,
|
||||
|
|
@ -134,13 +133,11 @@ async def test_session_experimental_get_task() -> None:
|
|||
|
||||
# Create a task
|
||||
create_result = await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
),
|
||||
CreateTaskResult,
|
||||
|
|
@ -240,13 +237,11 @@ async def test_session_experimental_get_task_result() -> None:
|
|||
|
||||
# Create a task
|
||||
create_result = await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
),
|
||||
CreateTaskResult,
|
||||
|
|
@ -343,13 +338,11 @@ async def test_session_experimental_list_tasks() -> None:
|
|||
# Create two tasks
|
||||
for _ in range(2):
|
||||
create_result = await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
),
|
||||
CreateTaskResult,
|
||||
|
|
@ -456,13 +449,11 @@ async def test_session_experimental_cancel_task() -> None:
|
|||
|
||||
# Create a task (but don't complete it)
|
||||
create_result = await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
),
|
||||
CreateTaskResult,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ from mcp.types import (
|
|||
CallToolRequest,
|
||||
CallToolRequestParams,
|
||||
CallToolResult,
|
||||
ClientRequest,
|
||||
ClientResult,
|
||||
CreateTaskResult,
|
||||
GetTaskPayloadRequest,
|
||||
|
|
@ -190,14 +189,12 @@ async def test_task_lifecycle_with_task_execution() -> None:
|
|||
|
||||
# === Step 1: Send task-augmented tool call ===
|
||||
create_result = await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="process_data",
|
||||
arguments={"input": "hello world"},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
),
|
||||
)
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="process_data",
|
||||
arguments={"input": "hello world"},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
),
|
||||
),
|
||||
CreateTaskResult,
|
||||
)
|
||||
|
|
@ -210,7 +207,7 @@ async def test_task_lifecycle_with_task_execution() -> None:
|
|||
await app_context.task_done_events[task_id].wait()
|
||||
|
||||
task_status = await client_session.send_request(
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))),
|
||||
GetTaskRequest(params=GetTaskRequestParams(task_id=task_id)),
|
||||
GetTaskResult,
|
||||
)
|
||||
|
||||
|
|
@ -219,7 +216,7 @@ async def test_task_lifecycle_with_task_execution() -> None:
|
|||
|
||||
# === Step 3: Retrieve the actual result ===
|
||||
task_result = await client_session.send_request(
|
||||
ClientRequest(GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task_id))),
|
||||
GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task_id)),
|
||||
CallToolResult,
|
||||
)
|
||||
|
||||
|
|
@ -327,14 +324,12 @@ async def test_task_auto_fails_on_exception() -> None:
|
|||
|
||||
# Send task request
|
||||
create_result = await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="failing_task",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
),
|
||||
)
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="failing_task",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
),
|
||||
),
|
||||
CreateTaskResult,
|
||||
)
|
||||
|
|
@ -346,8 +341,7 @@ async def test_task_auto_fails_on_exception() -> None:
|
|||
|
||||
# Check that task was auto-failed
|
||||
task_status = await client_session.send_request(
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))),
|
||||
GetTaskResult,
|
||||
GetTaskRequest(params=GetTaskRequestParams(task_id=task_id)), GetTaskResult
|
||||
)
|
||||
|
||||
assert task_status.status == "failed"
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ from mcp.types import (
|
|||
CancelTaskRequest,
|
||||
CancelTaskRequestParams,
|
||||
CancelTaskResult,
|
||||
ClientRequest,
|
||||
ClientResult,
|
||||
ErrorData,
|
||||
GetTaskPayloadRequest,
|
||||
|
|
@ -89,10 +88,10 @@ async def test_list_tasks_handler() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListTasksResult)
|
||||
assert len(result.root.tasks) == 2
|
||||
assert result.root.tasks[0].task_id == "task-1"
|
||||
assert result.root.tasks[1].task_id == "task-2"
|
||||
assert isinstance(result, ListTasksResult)
|
||||
assert len(result.tasks) == 2
|
||||
assert result.tasks[0].task_id == "task-1"
|
||||
assert result.tasks[1].task_id == "task-2"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -120,9 +119,9 @@ async def test_get_task_handler() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, GetTaskResult)
|
||||
assert result.root.task_id == "test-task-123"
|
||||
assert result.root.status == "working"
|
||||
assert isinstance(result, GetTaskResult)
|
||||
assert result.task_id == "test-task-123"
|
||||
assert result.status == "working"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -142,7 +141,7 @@ async def test_get_task_result_handler() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, GetTaskPayloadResult)
|
||||
assert isinstance(result, GetTaskPayloadResult)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -169,9 +168,9 @@ async def test_cancel_task_handler() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, CancelTaskResult)
|
||||
assert result.root.task_id == "test-task-123"
|
||||
assert result.root.status == "cancelled"
|
||||
assert isinstance(result, CancelTaskResult)
|
||||
assert result.task_id == "test-task-123"
|
||||
assert result.status == "cancelled"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -253,8 +252,8 @@ async def test_tool_with_task_execution_metadata() -> None:
|
|||
result = await tools_handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListToolsResult)
|
||||
tools = result.root.tools
|
||||
assert isinstance(result, ListToolsResult)
|
||||
tools = result.tools
|
||||
|
||||
assert tools[0].execution is not None
|
||||
assert tools[0].execution.task_support == TASK_FORBIDDEN
|
||||
|
|
@ -330,14 +329,12 @@ async def test_task_metadata_in_call_tool_request() -> None:
|
|||
|
||||
# Call tool with task metadata
|
||||
await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="long_task",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
),
|
||||
)
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="long_task",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
),
|
||||
),
|
||||
CallToolResult,
|
||||
)
|
||||
|
|
@ -411,24 +408,14 @@ async def test_task_metadata_is_task_property() -> None:
|
|||
|
||||
# Call without task metadata
|
||||
await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(name="test_tool", arguments={}),
|
||||
)
|
||||
),
|
||||
CallToolRequest(params=CallToolRequestParams(name="test_tool", arguments={})),
|
||||
CallToolResult,
|
||||
)
|
||||
|
||||
# Call with task metadata
|
||||
await client_session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(
|
||||
name="test_tool",
|
||||
arguments={},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
),
|
||||
)
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(name="test_tool", arguments={}, task=TaskMetadata(ttl=60000)),
|
||||
),
|
||||
CallToolResult,
|
||||
)
|
||||
|
|
@ -507,16 +494,13 @@ async def test_default_task_handlers_via_enable_tasks() -> None:
|
|||
task = await store.create_task(TaskMetadata(ttl=60000))
|
||||
|
||||
# Test list_tasks (default handler)
|
||||
list_result = await client_session.send_request(
|
||||
ClientRequest(ListTasksRequest()),
|
||||
ListTasksResult,
|
||||
)
|
||||
list_result = await client_session.send_request(ListTasksRequest(), ListTasksResult)
|
||||
assert len(list_result.tasks) == 1
|
||||
assert list_result.tasks[0].task_id == task.task_id
|
||||
|
||||
# Test get_task (default handler - found)
|
||||
get_result = await client_session.send_request(
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task.task_id))),
|
||||
GetTaskRequest(params=GetTaskRequestParams(task_id=task.task_id)),
|
||||
GetTaskResult,
|
||||
)
|
||||
assert get_result.task_id == task.task_id
|
||||
|
|
@ -525,7 +509,7 @@ async def test_default_task_handlers_via_enable_tasks() -> None:
|
|||
# Test get_task (default handler - not found path)
|
||||
with pytest.raises(McpError, match="not found"):
|
||||
await client_session.send_request(
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id="nonexistent-task"))),
|
||||
GetTaskRequest(params=GetTaskRequestParams(task_id="nonexistent-task")),
|
||||
GetTaskResult,
|
||||
)
|
||||
|
||||
|
|
@ -538,9 +522,7 @@ async def test_default_task_handlers_via_enable_tasks() -> None:
|
|||
|
||||
# Test get_task_result (default handler)
|
||||
payload_result = await client_session.send_request(
|
||||
ClientRequest(
|
||||
GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=completed_task.task_id))
|
||||
),
|
||||
GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=completed_task.task_id)),
|
||||
GetTaskPayloadResult,
|
||||
)
|
||||
# The result should have the related-task metadata
|
||||
|
|
@ -549,8 +531,7 @@ async def test_default_task_handlers_via_enable_tasks() -> None:
|
|||
|
||||
# Test cancel_task (default handler)
|
||||
cancel_result = await client_session.send_request(
|
||||
ClientRequest(CancelTaskRequest(params=CancelTaskRequestParams(task_id=task.task_id))),
|
||||
CancelTaskResult,
|
||||
CancelTaskRequest(params=CancelTaskRequestParams(task_id=task.task_id)), CancelTaskResult
|
||||
)
|
||||
assert cancel_result.task_id == task.task_id
|
||||
assert cancel_result.status == "cancelled"
|
||||
|
|
@ -568,11 +549,7 @@ async def test_build_elicit_form_request() -> None:
|
|||
async with ServerSession(
|
||||
client_to_server_receive,
|
||||
server_to_client_send,
|
||||
InitializationOptions(
|
||||
server_name="test-server",
|
||||
server_version="1.0.0",
|
||||
capabilities=ServerCapabilities(),
|
||||
),
|
||||
InitializationOptions(server_name="test-server", server_version="1.0.0", capabilities=ServerCapabilities()),
|
||||
) as server_session:
|
||||
# Test without task_id
|
||||
request = server_session._build_elicit_form_request(
|
||||
|
|
@ -613,11 +590,7 @@ async def test_build_elicit_url_request() -> None:
|
|||
async with ServerSession(
|
||||
client_to_server_receive,
|
||||
server_to_client_send,
|
||||
InitializationOptions(
|
||||
server_name="test-server",
|
||||
server_version="1.0.0",
|
||||
capabilities=ServerCapabilities(),
|
||||
),
|
||||
InitializationOptions(server_name="test-server", server_version="1.0.0", capabilities=ServerCapabilities()),
|
||||
) as server_session:
|
||||
# Test without related_task_id
|
||||
request = server_session._build_elicit_url_request(
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ async def test_resource_templates():
|
|||
result = await mcp._mcp_server.request_handlers[types.ListResourceTemplatesRequest](
|
||||
types.ListResourceTemplatesRequest(params=None)
|
||||
)
|
||||
assert isinstance(result.root, types.ListResourceTemplatesResult)
|
||||
templates = result.root.resource_templates
|
||||
assert isinstance(result, types.ListResourceTemplatesResult)
|
||||
templates = result.resource_templates
|
||||
|
||||
# Verify we get both templates back
|
||||
assert len(templates) == 2
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ async def test_server_base64_encoding_issue():
|
|||
result: ServerResult = await handler(request)
|
||||
|
||||
# After (fixed code):
|
||||
read_result: ReadResourceResult = cast(ReadResourceResult, result.root)
|
||||
read_result: ReadResourceResult = cast(ReadResourceResult, result)
|
||||
blob_content = read_result.contents[0]
|
||||
|
||||
# First verify our test data actually produces different encodings
|
||||
|
|
|
|||
|
|
@ -77,14 +77,14 @@ class NotificationCollector:
|
|||
) -> None:
|
||||
"""Handle any server notification and route to appropriate handler."""
|
||||
if isinstance(message, ServerNotification): # pragma: no branch
|
||||
if isinstance(message.root, ProgressNotification):
|
||||
self.progress_notifications.append(message.root.params)
|
||||
elif isinstance(message.root, LoggingMessageNotification):
|
||||
self.log_messages.append(message.root.params)
|
||||
elif isinstance(message.root, ResourceListChangedNotification):
|
||||
self.resource_notifications.append(message.root.params)
|
||||
elif isinstance(message.root, ToolListChangedNotification): # pragma: no cover
|
||||
self.tool_notifications.append(message.root.params)
|
||||
if isinstance(message, ProgressNotification):
|
||||
self.progress_notifications.append(message.params)
|
||||
elif isinstance(message, LoggingMessageNotification):
|
||||
self.log_messages.append(message.params)
|
||||
elif isinstance(message, ResourceListChangedNotification):
|
||||
self.resource_notifications.append(message.params)
|
||||
elif isinstance(message, ToolListChangedNotification): # pragma: no cover
|
||||
self.tool_notifications.append(message.params)
|
||||
|
||||
|
||||
# Common fixtures
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ async def test_list_prompts_basic() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListPromptsResult)
|
||||
assert result.root.prompts == test_prompts
|
||||
assert isinstance(result, ListPromptsResult)
|
||||
assert result.prompts == test_prompts
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -67,8 +67,8 @@ async def test_list_resources_basic() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListResourcesResult)
|
||||
assert result.root.resources == test_resources
|
||||
assert isinstance(result, ListResourcesResult)
|
||||
assert result.resources == test_resources
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -114,8 +114,8 @@ async def test_list_tools_basic() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListToolsResult)
|
||||
assert result.root.tools == test_tools
|
||||
assert isinstance(result, ListToolsResult)
|
||||
assert result.tools == test_tools
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -135,8 +135,8 @@ async def test_list_prompts_empty() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListPromptsResult)
|
||||
assert result.root.prompts == []
|
||||
assert isinstance(result, ListPromptsResult)
|
||||
assert result.prompts == []
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -156,8 +156,8 @@ async def test_list_resources_empty() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListResourcesResult)
|
||||
assert result.root.resources == []
|
||||
assert isinstance(result, ListResourcesResult)
|
||||
assert result.resources == []
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -177,5 +177,5 @@ async def test_list_tools_empty() -> None:
|
|||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListToolsResult)
|
||||
assert result.root.tools == []
|
||||
assert isinstance(result, ListToolsResult)
|
||||
assert result.tools == []
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ from mcp.types import (
|
|||
CallToolResult,
|
||||
CancelledNotification,
|
||||
CancelledNotificationParams,
|
||||
ClientNotification,
|
||||
ClientRequest,
|
||||
Tool,
|
||||
)
|
||||
|
||||
|
|
@ -59,11 +57,7 @@ async def test_server_remains_functional_after_cancel():
|
|||
async def first_request():
|
||||
try:
|
||||
await client.session.send_request(
|
||||
ClientRequest(
|
||||
CallToolRequest(
|
||||
params=CallToolRequestParams(name="test_tool", arguments={}),
|
||||
)
|
||||
),
|
||||
CallToolRequest(params=CallToolRequestParams(name="test_tool", arguments={})),
|
||||
CallToolResult,
|
||||
)
|
||||
pytest.fail("First request should have been cancelled") # pragma: no cover
|
||||
|
|
@ -80,13 +74,8 @@ async def test_server_remains_functional_after_cancel():
|
|||
# Cancel it
|
||||
assert first_request_id is not None
|
||||
await client.session.send_notification(
|
||||
ClientNotification(
|
||||
CancelledNotification(
|
||||
params=CancelledNotificationParams(
|
||||
request_id=first_request_id,
|
||||
reason="Testing server recovery",
|
||||
),
|
||||
)
|
||||
CancelledNotification(
|
||||
params=CancelledNotificationParams(request_id=first_request_id, reason="Testing server recovery"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ async def test_normal_message_handling_not_affected():
|
|||
|
||||
# Create a mock RequestResponder
|
||||
responder = Mock(spec=RequestResponder)
|
||||
responder.request = types.ClientRequest(root=types.PingRequest(method="ping"))
|
||||
responder.request = types.PingRequest(method="ping")
|
||||
responder.__enter__ = Mock(return_value=responder)
|
||||
responder.__exit__ = Mock(return_value=None)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ async def test_read_resource_text(temp_file: Path):
|
|||
|
||||
# Call the handler
|
||||
result = await handler(request)
|
||||
assert isinstance(result.root, types.ReadResourceResult)
|
||||
assert len(result.root.contents) == 1
|
||||
assert isinstance(result, types.ReadResourceResult)
|
||||
assert len(result.contents) == 1
|
||||
|
||||
content = result.root.contents[0]
|
||||
content = result.contents[0]
|
||||
assert isinstance(content, types.TextResourceContents)
|
||||
assert content.text == "Hello World"
|
||||
assert content.mime_type == "text/plain"
|
||||
|
|
@ -66,10 +66,10 @@ async def test_read_resource_binary(temp_file: Path):
|
|||
|
||||
# Call the handler
|
||||
result = await handler(request)
|
||||
assert isinstance(result.root, types.ReadResourceResult)
|
||||
assert len(result.root.contents) == 1
|
||||
assert isinstance(result, types.ReadResourceResult)
|
||||
assert len(result.contents) == 1
|
||||
|
||||
content = result.root.contents[0]
|
||||
content = result.contents[0]
|
||||
assert isinstance(content, types.BlobResourceContents)
|
||||
assert content.mime_type == "application/octet-stream"
|
||||
|
||||
|
|
@ -97,10 +97,10 @@ async def test_read_resource_default_mime(temp_file: Path):
|
|||
|
||||
# Call the handler
|
||||
result = await handler(request)
|
||||
assert isinstance(result.root, types.ReadResourceResult)
|
||||
assert len(result.root.contents) == 1
|
||||
assert isinstance(result, types.ReadResourceResult)
|
||||
assert len(result.contents) == 1
|
||||
|
||||
content = result.root.contents[0]
|
||||
content = result.contents[0]
|
||||
assert isinstance(content, types.TextResourceContents)
|
||||
assert content.text == "Hello World"
|
||||
assert content.mime_type == "text/plain"
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ async def test_server_session_initialize():
|
|||
raise message
|
||||
|
||||
if isinstance(message, ClientNotification) and isinstance(
|
||||
message.root, InitializedNotification
|
||||
message, InitializedNotification
|
||||
): # pragma: no branch
|
||||
received_initialized = True
|
||||
return
|
||||
|
|
@ -158,7 +158,7 @@ async def test_server_session_initialize_with_older_protocol_version():
|
|||
raise message
|
||||
|
||||
if isinstance(message, types.ClientNotification) and isinstance(
|
||||
message.root, InitializedNotification
|
||||
message, InitializedNotification
|
||||
): # pragma: no branch
|
||||
received_initialized = True
|
||||
return
|
||||
|
|
@ -236,11 +236,11 @@ async def test_ping_request_before_initialization():
|
|||
|
||||
# We should receive a ping request before initialization
|
||||
if isinstance(message, RequestResponder) and isinstance(
|
||||
message.request.root, types.PingRequest
|
||||
message.request, types.PingRequest
|
||||
): # pragma: no branch
|
||||
# Respond to the ping
|
||||
with message:
|
||||
await message.respond(types.ServerResult(types.EmptyResult()))
|
||||
await message.respond(types.EmptyResult())
|
||||
return
|
||||
|
||||
async def mock_client():
|
||||
|
|
|
|||
|
|
@ -57,27 +57,25 @@ async def test_request_immediately_after_initialize_response():
|
|||
|
||||
# Handle tools/list request
|
||||
if isinstance(message, RequestResponder):
|
||||
if isinstance(message.request.root, types.ListToolsRequest): # pragma: no branch
|
||||
if isinstance(message.request, types.ListToolsRequest): # pragma: no branch
|
||||
tools_list_success = True
|
||||
# Respond with a tool list
|
||||
with message:
|
||||
await message.respond(
|
||||
types.ServerResult(
|
||||
types.ListToolsResult(
|
||||
tools=[
|
||||
Tool(
|
||||
name="example_tool",
|
||||
description="An example tool",
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
)
|
||||
]
|
||||
)
|
||||
types.ListToolsResult(
|
||||
tools=[
|
||||
Tool(
|
||||
name="example_tool",
|
||||
description="An example tool",
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
# Handle InitializedNotification
|
||||
if isinstance(message, types.ClientNotification):
|
||||
if isinstance(message.root, types.InitializedNotification): # pragma: no branch
|
||||
if isinstance(message, types.InitializedNotification): # pragma: no branch
|
||||
# Done - exit gracefully
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -135,8 +135,8 @@ async def test_bidirectional_progress_notifications():
|
|||
raise message
|
||||
|
||||
if isinstance(message, types.ServerNotification): # pragma: no branch
|
||||
if isinstance(message.root, types.ProgressNotification): # pragma: no branch
|
||||
params = message.root.params
|
||||
if isinstance(message, types.ProgressNotification): # pragma: no branch
|
||||
params = message.params
|
||||
client_progress_updates.append(
|
||||
{
|
||||
"token": params.progress_token,
|
||||
|
|
@ -332,7 +332,7 @@ async def test_progress_callback_exception_logging():
|
|||
# Track logged warnings
|
||||
logged_errors: list[str] = []
|
||||
|
||||
def mock_log_error(msg: str, *args: Any) -> None:
|
||||
def mock_log_exception(msg: str, *args: Any, **kwargs: Any) -> None:
|
||||
logged_errors.append(msg % args if args else msg)
|
||||
|
||||
# Create a progress callback that raises an exception
|
||||
|
|
@ -368,7 +368,7 @@ async def test_progress_callback_exception_logging():
|
|||
]
|
||||
|
||||
# Test with mocked logging
|
||||
with patch("mcp.shared.session.logging.error", side_effect=mock_log_error):
|
||||
with patch("mcp.shared.session.logging.exception", side_effect=mock_log_exception):
|
||||
async with Client(server) as client:
|
||||
# Call tool with a failing progress callback
|
||||
result = await client.call_tool(
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ from mcp.shared.message import SessionMessage
|
|||
from mcp.types import (
|
||||
CancelledNotification,
|
||||
CancelledNotificationParams,
|
||||
ClientNotification,
|
||||
ClientRequest,
|
||||
EmptyResult,
|
||||
ErrorData,
|
||||
JSONRPCError,
|
||||
|
|
@ -73,10 +71,8 @@ async def test_request_cancellation():
|
|||
nonlocal ev_cancelled
|
||||
try:
|
||||
await client.session.send_request(
|
||||
ClientRequest(
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(name="slow_tool", arguments={}),
|
||||
)
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(name="slow_tool", arguments={}),
|
||||
),
|
||||
types.CallToolResult,
|
||||
)
|
||||
|
|
@ -97,11 +93,7 @@ async def test_request_cancellation():
|
|||
# Send cancellation notification
|
||||
assert request_id is not None
|
||||
await client.session.send_notification(
|
||||
ClientNotification(
|
||||
CancelledNotification(
|
||||
params=CancelledNotificationParams(request_id=request_id),
|
||||
)
|
||||
)
|
||||
CancelledNotification(params=CancelledNotificationParams(request_id=request_id))
|
||||
)
|
||||
|
||||
# Give cancellation time to process
|
||||
|
|
@ -252,7 +244,7 @@ async def test_response_id_non_numeric_string_no_match():
|
|||
try:
|
||||
# Use a short timeout since we expect this to fail
|
||||
await client_session.send_request(
|
||||
ClientRequest(types.PingRequest()),
|
||||
types.PingRequest(),
|
||||
types.EmptyResult,
|
||||
request_read_timeout_seconds=0.5,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1125,8 +1125,8 @@ async def test_streamable_http_client_get_stream(basic_server: None, basic_serve
|
|||
# Verify the notification is a ResourceUpdatedNotification
|
||||
resource_update_found = False
|
||||
for notif in notifications_received:
|
||||
if isinstance(notif.root, types.ResourceUpdatedNotification): # pragma: no branch
|
||||
assert str(notif.root.params.uri) == "http://test_resource"
|
||||
if isinstance(notif, types.ResourceUpdatedNotification): # pragma: no branch
|
||||
assert str(notif.params.uri) == "http://test_resource"
|
||||
resource_update_found = True
|
||||
|
||||
assert resource_update_found, "ResourceUpdatedNotification not received via GET stream"
|
||||
|
|
@ -1167,10 +1167,7 @@ async def test_streamable_http_client_session_termination(basic_server: None, ba
|
|||
):
|
||||
async with ClientSession(read_stream, write_stream) as session: # pragma: no branch
|
||||
# Attempt to make a request after termination
|
||||
with pytest.raises( # pragma: no branch
|
||||
McpError,
|
||||
match="Session terminated",
|
||||
):
|
||||
with pytest.raises(McpError, match="Session terminated"): # pragma: no branch
|
||||
await session.list_tools()
|
||||
|
||||
|
||||
|
|
@ -1259,8 +1256,8 @@ async def test_streamable_http_client_resumption(event_server: tuple[SimpleEvent
|
|||
if isinstance(message, types.ServerNotification): # pragma: no branch
|
||||
captured_notifications.append(message)
|
||||
# Look for our first notification
|
||||
if isinstance(message.root, types.LoggingMessageNotification): # pragma: no branch
|
||||
if message.root.params.data == "First notification before lock":
|
||||
if isinstance(message, types.LoggingMessageNotification): # pragma: no branch
|
||||
if message.params.data == "First notification before lock":
|
||||
nonlocal first_notification_received
|
||||
first_notification_received = True
|
||||
|
||||
|
|
@ -1291,12 +1288,8 @@ async def test_streamable_http_client_resumption(event_server: tuple[SimpleEvent
|
|||
on_resumption_token_update=on_resumption_token_update,
|
||||
)
|
||||
await session.send_request(
|
||||
types.ClientRequest(
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(
|
||||
name="wait_for_lock_with_notification", arguments={}
|
||||
),
|
||||
)
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(name="wait_for_lock_with_notification", arguments={}),
|
||||
),
|
||||
types.CallToolResult,
|
||||
metadata=metadata,
|
||||
|
|
@ -1313,8 +1306,8 @@ async def test_streamable_http_client_resumption(event_server: tuple[SimpleEvent
|
|||
|
||||
# Verify we received exactly one notification
|
||||
assert len(captured_notifications) == 1 # pragma: no cover
|
||||
assert isinstance(captured_notifications[0].root, types.LoggingMessageNotification) # pragma: no cover
|
||||
assert captured_notifications[0].root.params.data == "First notification before lock" # pragma: no cover
|
||||
assert isinstance(captured_notifications[0], types.LoggingMessageNotification) # pragma: no cover
|
||||
assert captured_notifications[0].params.data == "First notification before lock" # pragma: no cover
|
||||
|
||||
# Clear notifications for the second phase
|
||||
captured_notifications = [] # pragma: no cover
|
||||
|
|
@ -1334,11 +1327,7 @@ async def test_streamable_http_client_resumption(event_server: tuple[SimpleEvent
|
|||
):
|
||||
async with ClientSession(read_stream, write_stream, message_handler=message_handler) as session:
|
||||
result = await session.send_request(
|
||||
types.ClientRequest(
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(name="release_lock", arguments={}),
|
||||
)
|
||||
),
|
||||
types.CallToolRequest(params=types.CallToolRequestParams(name="release_lock", arguments={})),
|
||||
types.CallToolResult,
|
||||
)
|
||||
metadata = ClientMessageMetadata(
|
||||
|
|
@ -1346,10 +1335,8 @@ async def test_streamable_http_client_resumption(event_server: tuple[SimpleEvent
|
|||
)
|
||||
|
||||
result = await session.send_request(
|
||||
types.ClientRequest(
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(name="wait_for_lock_with_notification", arguments={}),
|
||||
)
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(name="wait_for_lock_with_notification", arguments={}),
|
||||
),
|
||||
types.CallToolResult,
|
||||
metadata=metadata,
|
||||
|
|
@ -1361,8 +1348,8 @@ async def test_streamable_http_client_resumption(event_server: tuple[SimpleEvent
|
|||
# We should have received the remaining notifications
|
||||
assert len(captured_notifications) == 1
|
||||
|
||||
assert isinstance(captured_notifications[0].root, types.LoggingMessageNotification) # pragma: no cover
|
||||
assert captured_notifications[0].root.params.data == "Second notification after lock" # pragma: no cover
|
||||
assert isinstance(captured_notifications[0], types.LoggingMessageNotification) # pragma: no cover
|
||||
assert captured_notifications[0].params.data == "Second notification after lock" # pragma: no cover
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -1905,11 +1892,7 @@ async def test_streamable_http_client_receives_priming_event(
|
|||
on_resumption_token_update=on_resumption_token_update,
|
||||
)
|
||||
result = await session.send_request(
|
||||
types.ClientRequest(
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(name="test_tool", arguments={}),
|
||||
)
|
||||
),
|
||||
types.CallToolRequest(params=types.CallToolRequestParams(name="test_tool", arguments={})),
|
||||
types.CallToolResult,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
|
@ -1967,8 +1950,8 @@ async def test_streamable_http_client_auto_reconnects(
|
|||
if isinstance(message, Exception): # pragma: no branch
|
||||
return # pragma: no cover
|
||||
if isinstance(message, types.ServerNotification): # pragma: no branch
|
||||
if isinstance(message.root, types.LoggingMessageNotification): # pragma: no branch
|
||||
captured_notifications.append(str(message.root.params.data))
|
||||
if isinstance(message, types.LoggingMessageNotification): # pragma: no branch
|
||||
captured_notifications.append(str(message.params.data))
|
||||
|
||||
async with streamable_http_client(f"{server_url}/mcp") as (
|
||||
read_stream,
|
||||
|
|
@ -2043,8 +2026,8 @@ async def test_streamable_http_sse_polling_full_cycle(
|
|||
if isinstance(message, Exception): # pragma: no branch
|
||||
return # pragma: no cover
|
||||
if isinstance(message, types.ServerNotification): # pragma: no branch
|
||||
if isinstance(message.root, types.LoggingMessageNotification): # pragma: no branch
|
||||
all_notifications.append(str(message.root.params.data))
|
||||
if isinstance(message, types.LoggingMessageNotification): # pragma: no branch
|
||||
all_notifications.append(str(message.params.data))
|
||||
|
||||
async with streamable_http_client(f"{server_url}/mcp") as (
|
||||
read_stream,
|
||||
|
|
@ -2091,8 +2074,8 @@ async def test_streamable_http_events_replayed_after_disconnect(
|
|||
if isinstance(message, Exception): # pragma: no branch
|
||||
return # pragma: no cover
|
||||
if isinstance(message, types.ServerNotification): # pragma: no branch
|
||||
if isinstance(message.root, types.LoggingMessageNotification): # pragma: no branch
|
||||
notification_data.append(str(message.root.params.data))
|
||||
if isinstance(message, types.LoggingMessageNotification): # pragma: no branch
|
||||
notification_data.append(str(message.params.data))
|
||||
|
||||
async with streamable_http_client(f"{server_url}/mcp") as (
|
||||
read_stream,
|
||||
|
|
@ -2153,15 +2136,13 @@ async def test_streamable_http_multiple_reconnections(
|
|||
# Use send_request with metadata to track resumption tokens
|
||||
metadata = ClientMessageMetadata(on_resumption_token_update=on_resumption_token)
|
||||
result = await session.send_request(
|
||||
types.ClientRequest(
|
||||
types.CallToolRequest(
|
||||
method="tools/call",
|
||||
params=types.CallToolRequestParams(
|
||||
name="tool_with_multiple_stream_closes",
|
||||
# retry_interval=500ms, so sleep 600ms to ensure reconnect completes
|
||||
arguments={"checkpoints": 3, "sleep_time": 0.6},
|
||||
),
|
||||
)
|
||||
types.CallToolRequest(
|
||||
method="tools/call",
|
||||
params=types.CallToolRequestParams(
|
||||
name="tool_with_multiple_stream_closes",
|
||||
# retry_interval=500ms, so sleep 600ms to ensure reconnect completes
|
||||
arguments={"checkpoints": 3, "sleep_time": 0.6},
|
||||
),
|
||||
),
|
||||
types.CallToolResult,
|
||||
metadata=metadata,
|
||||
|
|
@ -2202,8 +2183,8 @@ async def test_standalone_get_stream_reconnection(
|
|||
if isinstance(message, Exception):
|
||||
return # pragma: no cover
|
||||
if isinstance(message, types.ServerNotification): # pragma: no branch
|
||||
if isinstance(message.root, types.ResourceUpdatedNotification): # pragma: no branch
|
||||
received_notifications.append(str(message.root.params.uri))
|
||||
if isinstance(message, types.ResourceUpdatedNotification): # pragma: no branch
|
||||
received_notifications.append(str(message.params.uri))
|
||||
|
||||
async with streamable_http_client(f"{server_url}/mcp") as (
|
||||
read_stream,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import pytest
|
|||
from mcp.types import (
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
ClientCapabilities,
|
||||
ClientRequest,
|
||||
CreateMessageRequestParams,
|
||||
CreateMessageResult,
|
||||
CreateMessageResultWithTools,
|
||||
|
|
@ -21,6 +20,7 @@ from mcp.types import (
|
|||
ToolChoice,
|
||||
ToolResultContent,
|
||||
ToolUseContent,
|
||||
client_request_adapter,
|
||||
jsonrpc_message_adapter,
|
||||
)
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ async def test_jsonrpc_request():
|
|||
|
||||
request = jsonrpc_message_adapter.validate_python(json_data)
|
||||
assert isinstance(request, JSONRPCRequest)
|
||||
ClientRequest.model_validate(request.model_dump(by_alias=True, exclude_none=True))
|
||||
client_request_adapter.validate_python(request.model_dump(by_alias=True, exclude_none=True))
|
||||
|
||||
assert request.jsonrpc == "2.0"
|
||||
assert request.id == 1
|
||||
|
|
|
|||
Loading…
Reference in a new issue