Files
modelcontextprotocol--pytho…/tests/docs_src/test_context.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

89 lines
4.1 KiB
Python

"""`docs/handlers/context.md`: every claim the page makes, proved against the real SDK."""
import re
import pytest
from inline_snapshot import snapshot
from mcp_types import TextContent, TextResourceContents, ToolListChangedNotification
from docs_src.context import tutorial001, tutorial002, tutorial003
from mcp import Client
# 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_context_parameter_is_not_in_the_input_schema() -> None:
"""tutorial001: the injected `Context` never appears in the schema the model sees."""
async with Client(tutorial001.mcp) as client:
(tool,) = (await client.list_tools()).tools
assert tool.input_schema == snapshot(
{
"type": "object",
"properties": {"query": {"title": "Query", "type": "string"}},
"required": ["query"],
"title": "search_booksArguments",
}
)
async def test_every_request_gets_its_own_context() -> None:
"""tutorial001: `ctx.request_id` identifies the request being served, so it changes per call."""
async with Client(tutorial001.mcp) as client:
first = await client.call_tool("search_books", {"query": "dune"})
second = await client.call_tool("search_books", {"query": "dune"})
assert isinstance(first.content[0], TextContent)
assert isinstance(second.content[0], TextContent)
assert re.fullmatch(r"\[request \d+\] Found 3 books matching 'dune'\.", first.content[0].text)
assert first.content[0].text != second.content[0].text
async def test_a_tool_reads_the_servers_own_resource() -> None:
"""tutorial002: `ctx.read_resource` resolves the URI through the same registry `resources/read` uses."""
async with Client(tutorial002.mcp) as client:
result = await client.call_tool("describe_catalog", {})
assert not result.is_error
assert result.content == [
TextContent(type="text", text="The catalog is organised into: fiction, non-fiction, poetry")
]
(contents,) = (await client.read_resource("catalog://genres")).contents
assert isinstance(contents, TextResourceContents)
assert contents.text == "fiction, non-fiction, poetry"
async def test_a_context_only_tool_takes_no_arguments() -> None:
"""tutorial002: a tool whose only parameter is the `Context` has an empty input schema."""
async with Client(tutorial002.mcp) as client:
tools = {tool.name: tool for tool in (await client.list_tools()).tools}
assert tools["describe_catalog"].input_schema == snapshot(
{"type": "object", "properties": {}, "title": "describe_catalogArguments"}
)
async def test_register_a_tool_at_runtime_and_notify_the_client() -> None:
"""tutorial003: `mcp.add_tool` takes effect immediately and `send_tool_list_changed` reaches the client."""
messages: list[object] = []
async def collect(message: object) -> None:
messages.append(message)
async with Client(tutorial003.mcp, mode="legacy", message_handler=collect) as client:
assert [tool.name for tool in (await client.list_tools()).tools] == ["enable_recommendations"]
missing = await client.call_tool("recommend_book", {"genre": "fiction"})
assert missing.is_error
assert missing.content == [TextContent(type="text", text="Unknown tool: recommend_book")]
enabled = await client.call_tool("enable_recommendations", {})
assert enabled.content == [TextContent(type="text", text="Recommendations are now available.")]
assert [tool.name for tool in (await client.list_tools()).tools] == [
"enable_recommendations",
"recommend_book",
]
result = await client.call_tool("recommend_book", {"genre": "fiction"})
assert result.content == [TextContent(type="text", text="In fiction, try 'Dune'.")]
(notification,) = messages
assert isinstance(notification, ToolListChangedNotification)