Files
modelcontextprotocol--pytho…/tests/client/test_session_promotions.py
T
wehub-resource-sync 49b9bb6724
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:10:27 +08:00

67 lines
2.9 KiB
Python

"""`dispatch_input_request` and `validate_tool_result` are public `ClientSession` API."""
import mcp_types as types
import pytest
from mcp_types import (
CallToolResult,
ErrorData,
ListRootsResult,
ListToolsResult,
PaginatedRequestParams,
Tool,
)
from mcp.client.client import Client
from mcp.client.session import ClientRequestContext, ClientSession
from mcp.server import Server, ServerRequestContext
from mcp.shared.direct_dispatcher import create_direct_dispatcher_pair
@pytest.mark.anyio
async def test_dispatch_input_request_routes_through_the_callback_table() -> None:
expected = ListRootsResult(roots=[])
async def list_roots(context: ClientRequestContext) -> ListRootsResult:
return expected
client_side, _server_side = create_direct_dispatcher_pair()
session = ClientSession(dispatcher=client_side, list_roots_callback=list_roots)
ctx = ClientRequestContext(session=session, request_id="r-1")
response = await session.dispatch_input_request(ctx, types.ListRootsRequest())
assert response is expected
@pytest.mark.anyio
async def test_dispatch_input_request_returns_error_data_on_refusal() -> None:
"""With no callback registered, refusal comes back as `ErrorData`, not a raise."""
client_side, _server_side = create_direct_dispatcher_pair()
session = ClientSession(dispatcher=client_side)
ctx = ClientRequestContext(session=session, request_id="r-1")
response = await session.dispatch_input_request(ctx, types.ListRootsRequest())
assert isinstance(response, ErrorData)
assert response.code == types.INVALID_REQUEST
def _make_server(output_schema: dict[str, object]) -> Server:
async def on_list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
return ListToolsResult(tools=[Tool(name="t", input_schema={"type": "object"}, output_schema=output_schema)])
return Server("test-server", on_list_tools=on_list_tools)
@pytest.mark.anyio
async def test_validate_tool_result_passes_a_conforming_result() -> None:
server = _make_server({"type": "object", "properties": {"x": {"type": "integer"}}, "required": ["x"]})
async with Client(server) as client:
# The session fetches the listing itself when the tool isn't cached yet.
await client.session.validate_tool_result("t", CallToolResult(content=[], structured_content={"x": 1}))
@pytest.mark.anyio
async def test_validate_tool_result_raises_on_schema_mismatch() -> None:
server = _make_server({"type": "object", "properties": {"x": {"type": "integer"}}, "required": ["x"]})
async with Client(server) as client:
# Stable SDK prefix only: the message tail is jsonschema text that shifts with the dependency.
with pytest.raises(RuntimeError, match="Invalid structured content returned by tool t"):
await client.session.validate_tool_result("t", CallToolResult(content=[], structured_content={"x": "no"}))