Files
modelcontextprotocol--pytho…/tests/docs_src/test_whats_new.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) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:10:27 +08:00

66 lines
2.9 KiB
Python

"""`docs/whats-new.md`: the v2 half of the low-level before/after example, proved against the real SDK.
The v1 half of that example targets the 1.x line and cannot run here; it was
validated by running it verbatim against a real `mcp==1.28.1` install.
"""
import pytest
from mcp_types import INTERNAL_ERROR, INVALID_PARAMS, TextContent
from docs_src.whats_new import tutorial001
from mcp import Client, MCPError
# See test_index.py for why this is a per-module mark and not a conftest hook.
pytestmark = [pytest.mark.anyio, pytest.mark.filterwarnings("error::mcp.MCPDeprecationWarning")]
async def test_the_advertised_schema_is_the_literal_dict() -> None:
"""Annotation 1: the schema is advertised to clients exactly as written."""
async with Client(tutorial001.server) as client:
(tool,) = (await client.list_tools()).tools
assert tool.name == "search_books"
assert tool.input_schema == {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
}
async def test_a_valid_call_answers() -> None:
"""The example works end to end through the in-process `Client`."""
async with Client(tutorial001.server) as client:
result = await client.call_tool("search_books", {"query": "dune"})
assert not result.is_error
assert result.content == [TextContent(type="text", text="Found 3 books matching 'dune'.")]
async def test_arguments_are_not_validated_and_a_handler_exception_is_sanitized() -> None:
"""Annotations 1, 6, and 7, in one flow.
A call missing the required `query` REACHES the handler (nothing validates
arguments against `input_schema`; v1 rejected this call before the handler
ran). The handler's own `KeyError` then comes back as a sanitized protocol
error, never an `is_error=True` result the model could read. A call with no
arguments at all exercises `params.arguments or {}` the same way.
"""
async with Client(tutorial001.server) as client:
with pytest.raises(MCPError) as excinfo:
await client.call_tool("search_books", {"limit": 5})
assert excinfo.value.code == INTERNAL_ERROR
assert excinfo.value.message == "Internal server error"
with pytest.raises(MCPError) as excinfo:
await client.call_tool("search_books")
assert excinfo.value.code == INTERNAL_ERROR
assert excinfo.value.message == "Internal server error"
async def test_an_unknown_tool_is_a_deliberate_wire_error() -> None:
"""Annotation 5: a raised `MCPError` passes through with its code and message
intact (the spec's answer for an unknown tool), unlike the sanitized path."""
async with Client(tutorial001.server) as client:
with pytest.raises(MCPError) as excinfo:
await client.call_tool("shelve_book", {"query": "dune"})
assert excinfo.value.code == INVALID_PARAMS
assert excinfo.value.message == "Unknown tool: shelve_book"