This commit is contained in:
parent
3a6f2996cd
commit
837ef904f8
13 changed files with 1757 additions and 567 deletions
23
tests/docs_src/_helpers.py
Normal file
23
tests/docs_src/_helpers.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
"""Shared helpers for the docs_src tests."""
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
from mcp_types import SERVER_INFO_META_KEY, Result
|
||||
|
||||
from mcp.server import Server
|
||||
from mcp.server.mcpserver import MCPServer
|
||||
|
||||
R = TypeVar("R", bound=Result)
|
||||
|
||||
|
||||
def strip_server_info(result: R, server: Server | MCPServer) -> R:
|
||||
"""Assert the 2026-era serverInfo stamp, then drop it so snapshots stay focused.
|
||||
|
||||
The doc snippets set no explicit version, so the stamp's version is empty;
|
||||
the fenced outputs in the docs pages leave the stamp out, and the tests
|
||||
mirror the fences.
|
||||
"""
|
||||
assert result.meta is not None
|
||||
assert result.meta[SERVER_INFO_META_KEY] == {"name": server.name, "version": ""}
|
||||
result.meta = None
|
||||
return result
|
||||
|
|
@ -27,6 +27,7 @@ async def test_every_client_program_on_the_page_runs(capsys: pytest.CaptureFixtu
|
|||
async def test_connected_properties_are_populated_inside_the_block() -> None:
|
||||
"""tutorial001: server_info, server_capabilities, protocol_version and instructions are just there."""
|
||||
async with Client(tutorial001.mcp) as client:
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "Bookshop"
|
||||
assert client.protocol_version == "2026-07-28"
|
||||
assert client.instructions == "Search the catalog before recommending a book."
|
||||
|
|
@ -38,6 +39,7 @@ async def test_a_client_is_not_reusable_after_the_block_ends() -> None:
|
|||
"""tutorial001: `async with` is the whole lifecycle. Construct a new Client per connection."""
|
||||
client = Client(tutorial001.mcp)
|
||||
async with client:
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "Bookshop"
|
||||
with pytest.raises(RuntimeError, match="cannot reenter"):
|
||||
await client.__aenter__()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ async def test_the_in_memory_program_on_the_page_runs(capsys: pytest.CaptureFixt
|
|||
async def test_in_memory_client_talks_to_the_server_object() -> None:
|
||||
"""tutorial001: passing the server object connects in-process. No subprocess, no port."""
|
||||
async with Client(tutorial001.mcp) as client:
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "Bookshop"
|
||||
assert client.protocol_version == "2026-07-28"
|
||||
result = await client.call_tool("search_books", {"query": "dune"})
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from mcp_types import CallToolResult, TextContent, TextResourceContents
|
|||
|
||||
from docs_src.index.tutorial001 import mcp
|
||||
from mcp import Client
|
||||
from tests.docs_src._helpers import strip_server_info
|
||||
|
||||
# `pyproject.toml` globally downgrades `mcp.MCPDeprecationWarning` to *ignore* because the
|
||||
# SDK still calls those methods internally. A documentation example must never lean on
|
||||
|
|
@ -18,6 +19,7 @@ pytestmark = [pytest.mark.anyio, pytest.mark.filterwarnings("error::mcp.MCPDepre
|
|||
async def test_add_tool() -> None:
|
||||
async with Client(mcp) as client:
|
||||
result = await client.call_tool("add", {"a": 1, "b": 2})
|
||||
result = strip_server_info(result, mcp)
|
||||
assert result == snapshot(
|
||||
CallToolResult(content=[TextContent(type="text", text="3")], structured_content={"result": 3})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from mcp_types import CallToolResult, TextContent
|
|||
from docs_src.logging import tutorial001
|
||||
from mcp import Client
|
||||
from mcp.server import MCPServer
|
||||
from tests.docs_src._helpers import strip_server_info
|
||||
|
||||
# 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")]
|
||||
|
|
@ -28,6 +29,7 @@ async def test_the_log_line_never_reaches_the_client() -> None:
|
|||
"""tutorial001: the result is only the return value. Log output is invisible to the model."""
|
||||
async with Client(tutorial001.mcp) as client:
|
||||
result = await client.call_tool("search_books", {"query": "dune"})
|
||||
result = strip_server_info(result, tutorial001.mcp)
|
||||
assert result == snapshot(
|
||||
CallToolResult(
|
||||
content=[TextContent(type="text", text="Found 3 books matching 'dune'.")],
|
||||
|
|
|
|||
|
|
@ -2,7 +2,15 @@
|
|||
|
||||
import pytest
|
||||
from inline_snapshot import snapshot
|
||||
from mcp_types import INTERNAL_ERROR, CallToolRequestParams, CallToolResult, ErrorData, RequestParams, TextContent
|
||||
from mcp_types import (
|
||||
INTERNAL_ERROR,
|
||||
SERVER_INFO_META_KEY,
|
||||
CallToolRequestParams,
|
||||
CallToolResult,
|
||||
ErrorData,
|
||||
RequestParams,
|
||||
TextContent,
|
||||
)
|
||||
|
||||
from docs_src.lowlevel import tutorial001, tutorial002, tutorial003, tutorial004, tutorial005, tutorial006
|
||||
from mcp import Client, MCPError
|
||||
|
|
@ -79,8 +87,17 @@ async def test_output_schema_and_structured_content_are_both_yours_to_build() ->
|
|||
}
|
||||
)
|
||||
result = await client.call_tool("search_books", {"query": "dune", "limit": 5})
|
||||
assert result.content == [TextContent(type="text", text="Found 3 books matching 'dune'.")]
|
||||
assert result.structured_content == {"matches": 3, "query": "dune"}
|
||||
# The page shows this exact payload; tutorial003 pins `version="2.0.0"` so
|
||||
# the identity stamp is deterministic and the fence is proved verbatim.
|
||||
assert result.model_dump(by_alias=True, exclude_none=True) == snapshot(
|
||||
{
|
||||
"_meta": {"io.modelcontextprotocol/serverInfo": {"name": "Bookshop", "version": "2.0.0"}},
|
||||
"content": [{"type": "text", "text": "Found 3 books matching 'dune'."}],
|
||||
"structuredContent": {"matches": 3, "query": "dune"},
|
||||
"isError": False,
|
||||
"resultType": "complete",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def test_the_client_checks_the_schema_you_promised() -> None:
|
||||
|
|
@ -99,6 +116,10 @@ async def test_meta_reaches_the_client_application() -> None:
|
|||
"""tutorial004: `_meta=` on the result comes back as `result.meta` and serialises under `_meta`."""
|
||||
async with Client(tutorial004.server) as client:
|
||||
result = await client.call_tool("search_books", {"query": "dune", "limit": 5})
|
||||
assert result.meta is not None
|
||||
# The server identity stamp shares `_meta` with the handler's keys without clobbering
|
||||
# them. Remove it before the exact compares: the page's fence leaves the stamp out.
|
||||
del result.meta[SERVER_INFO_META_KEY]
|
||||
assert result.meta == {"bookshop/record_ids": ["bk_17", "bk_42", "bk_99"]}
|
||||
assert result.model_dump(by_alias=True, exclude_none=True) == snapshot(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ def test_raw_data_without_a_format_falls_back_to_a_default_mime_type() -> None:
|
|||
async def test_icons_are_visible_where_they_were_declared() -> None:
|
||||
"""tutorial004: server icons land on `server_info`, tool icons on the `Tool`, resource icons on the `Resource`."""
|
||||
async with Client(tutorial004.mcp) as client:
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.icons == [
|
||||
Icon(src="https://example.com/brand-kit.png", mime_type="image/png", sizes=["48x48"])
|
||||
]
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ from docs_src.mrtr import tutorial001, tutorial002, tutorial003, tutorial004, tu
|
|||
from mcp import Client, MCPError
|
||||
from mcp.client import ClientRequestContext
|
||||
from mcp.server.mcpserver import InvalidRequestState
|
||||
from tests.docs_src._helpers import strip_server_info
|
||||
|
||||
# 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")]
|
||||
|
|
@ -31,6 +32,7 @@ async def test_first_call_returns_an_input_required_result() -> None:
|
|||
"""tutorial001: a tool that is missing input returns `InputRequiredResult` instead of calling back."""
|
||||
async with Client(tutorial001.server) as client:
|
||||
result = await client.session.call_tool("provision", {"name": "orders"}, allow_input_required=True)
|
||||
result = strip_server_info(result, tutorial001.server)
|
||||
assert result == snapshot(
|
||||
InputRequiredResult(
|
||||
result_type="input_required",
|
||||
|
|
@ -57,6 +59,7 @@ async def test_the_auto_loop_drives_the_call_to_completion() -> None:
|
|||
"""tutorial003: register `elicitation_callback`, call the tool, get a plain `CallToolResult` back."""
|
||||
async with Client(tutorial001.server, elicitation_callback=tutorial003.handle_elicitation) as client:
|
||||
result = await client.call_tool("provision", {"name": "orders"})
|
||||
result = strip_server_info(result, tutorial001.server)
|
||||
assert result == snapshot(
|
||||
CallToolResult(content=[TextContent(type="text", text="Provisioned 'orders' in eu-west-1.")])
|
||||
)
|
||||
|
|
@ -80,6 +83,7 @@ async def test_retry_with_input_responses_and_request_state_completes_the_call()
|
|||
input_responses={"region": ElicitResult(action="accept", content={"region": "eu-west-1"})},
|
||||
request_state="provision-v1",
|
||||
)
|
||||
result = strip_server_info(result, tutorial001.server)
|
||||
assert result == snapshot(
|
||||
CallToolResult(content=[TextContent(type="text", text="Provisioned 'orders' in eu-west-1.")])
|
||||
)
|
||||
|
|
@ -89,6 +93,7 @@ async def test_the_manual_loop_drives_the_call_to_completion() -> None:
|
|||
"""tutorial002: `client.session.call_tool(..., allow_input_required=True)` for callers who own the loop."""
|
||||
async with Client(tutorial001.server) as client:
|
||||
result = await tutorial002.provision(client, "billing")
|
||||
result = strip_server_info(result, tutorial001.server)
|
||||
assert result == snapshot(
|
||||
CallToolResult(content=[TextContent(type="text", text="Provisioned 'billing' in eu-west-1.")])
|
||||
)
|
||||
|
|
@ -121,6 +126,7 @@ async def test_a_prompt_returns_an_input_required_result_on_the_first_round() ->
|
|||
returns the `InputRequiredResult` itself."""
|
||||
async with Client(tutorial004.mcp) as client:
|
||||
result = await client.session.get_prompt("briefing", allow_input_required=True)
|
||||
result = strip_server_info(result, tutorial004.mcp)
|
||||
assert result == snapshot(
|
||||
InputRequiredResult(
|
||||
result_type="input_required",
|
||||
|
|
@ -151,6 +157,7 @@ async def test_the_prompt_auto_loop_returns_the_final_messages() -> None:
|
|||
caller sees only the complete `GetPromptResult`."""
|
||||
async with Client(tutorial004.mcp, elicitation_callback=_answer_audience) as client:
|
||||
result = await client.get_prompt("briefing")
|
||||
result = strip_server_info(result, tutorial004.mcp)
|
||||
assert result == snapshot(
|
||||
GetPromptResult(
|
||||
description="Draft a briefing tuned to its audience.",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from mcp_types import PromptArgument, PromptMessage, TextContent
|
|||
|
||||
from docs_src.prompts import tutorial001, tutorial002, tutorial003
|
||||
from mcp import Client, MCPError
|
||||
from tests.docs_src._helpers import strip_server_info
|
||||
|
||||
# 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")]
|
||||
|
|
@ -30,6 +31,7 @@ async def test_returned_string_becomes_one_user_message() -> None:
|
|||
"""tutorial001: a `str` return value is rendered as a single `user` message."""
|
||||
async with Client(tutorial001.mcp) as client:
|
||||
result = await client.get_prompt("review_code", {"code": "def add(a, b): return a + b"})
|
||||
result = strip_server_info(result, tutorial001.mcp)
|
||||
assert result.model_dump(mode="json", by_alias=True, exclude_none=True) == snapshot(
|
||||
{
|
||||
"description": "Review a piece of code.",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import re
|
||||
|
||||
import pytest
|
||||
from mcp_types import DiscoverResult, Implementation, ServerCapabilities
|
||||
from mcp_types import SERVER_INFO_META_KEY, DiscoverResult, Implementation, ServerCapabilities
|
||||
|
||||
from docs_src.protocol_versions import tutorial001, tutorial002, tutorial003, tutorial004
|
||||
from mcp import Client
|
||||
|
|
@ -16,6 +16,7 @@ async def test_auto_lands_on_the_modern_version() -> None:
|
|||
"""tutorial001: the default `mode="auto"` probes `server/discover` and adopts the result."""
|
||||
async with Client(tutorial001.mcp) as client:
|
||||
assert client.protocol_version == "2026-07-28"
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "Bookshop"
|
||||
assert client.session.discover_result is not None
|
||||
assert client.session.initialize_result is None
|
||||
|
|
@ -25,18 +26,18 @@ async def test_legacy_forces_the_initialize_handshake() -> None:
|
|||
"""tutorial002: `mode="legacy"` runs `initialize` against the very same server."""
|
||||
async with Client(tutorial002.mcp, mode="legacy") as client:
|
||||
assert client.protocol_version == "2025-11-25"
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "Bookshop"
|
||||
assert client.session.initialize_result is not None
|
||||
assert client.session.discover_result is None
|
||||
|
||||
|
||||
async def test_version_pin_sends_nothing_and_knows_nothing() -> None:
|
||||
"""tutorial003: a pin adopts the version locally; `server_info` and capabilities are blank."""
|
||||
"""tutorial003: a pin adopts the version locally; `server_info` is None and capabilities are blank."""
|
||||
async with Client(tutorial003.mcp, mode="2026-07-28") as client:
|
||||
assert client.protocol_version == "2026-07-28"
|
||||
assert client.server_info == Implementation(name="", version="")
|
||||
# The `!!! check` fence is the literal `print(client.server_info)` output.
|
||||
assert str(client.server_info) == "name='' title=None version='' description=None website_url=None icons=None"
|
||||
# The `!!! check` fence is the literal `print(client.server_info)` output: None.
|
||||
assert client.server_info is None
|
||||
assert client.server_capabilities == ServerCapabilities()
|
||||
result = await client.call_tool("search_books", {"query": "dune"})
|
||||
assert result.structured_content == {"result": "Found 3 books matching 'dune'."}
|
||||
|
|
@ -63,6 +64,7 @@ async def test_prior_discover_round_trips() -> None:
|
|||
|
||||
async with Client(tutorial004.mcp, mode="2026-07-28", prior_discover=saved) as client:
|
||||
assert client.protocol_version == "2026-07-28"
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "Bookshop"
|
||||
assert client.server_capabilities.tools is not None
|
||||
|
||||
|
|
@ -77,6 +79,7 @@ async def test_discover_result_survives_json() -> None:
|
|||
assert restored == saved
|
||||
|
||||
async with Client(tutorial004.mcp, mode="2026-07-28", prior_discover=restored) as client:
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "Bookshop"
|
||||
|
||||
|
||||
|
|
@ -85,9 +88,14 @@ async def test_prior_discover_is_ignored_unless_mode_is_a_pin() -> None:
|
|||
stale = DiscoverResult(
|
||||
supported_versions=["2026-07-28"],
|
||||
capabilities=ServerCapabilities(),
|
||||
server_info=Implementation(name="Stale", version="0.0.0"),
|
||||
_meta={
|
||||
SERVER_INFO_META_KEY: Implementation(name="Stale", version="0.0.0").model_dump(
|
||||
by_alias=True, mode="json", exclude_none=True
|
||||
)
|
||||
},
|
||||
)
|
||||
async with Client(tutorial004.mcp, prior_discover=stale) as client:
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "Bookshop"
|
||||
async with Client(tutorial004.mcp, mode="legacy", prior_discover=stale) as client:
|
||||
assert client.session.discover_result is None
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from mcp_types import CallToolResult, TextContent
|
|||
from docs_src.run import tutorial001, tutorial002, tutorial003
|
||||
from mcp import Client
|
||||
from mcp.server import MCPServer
|
||||
from tests.docs_src._helpers import strip_server_info
|
||||
|
||||
# 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")]
|
||||
|
|
@ -18,6 +19,7 @@ async def test_the_run_call_is_guarded_so_importing_does_not_start_a_server() ->
|
|||
"""tutorial001: `run()` sits under `__main__`, so the module imports cleanly and serves in-memory."""
|
||||
async with Client(tutorial001.mcp) as client:
|
||||
result = await client.call_tool("search_books", {"query": "dune"})
|
||||
result = strip_server_info(result, tutorial001.mcp)
|
||||
assert result == snapshot(
|
||||
CallToolResult(
|
||||
content=[TextContent(type="text", text="Found 3 books matching 'dune'.")],
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@ from mcp import Client, ClientSessionGroup, MCPError
|
|||
pytestmark = [pytest.mark.anyio, pytest.mark.filterwarnings("error::mcp.MCPDeprecationWarning")]
|
||||
|
||||
|
||||
def _server_info(client: Client) -> Implementation:
|
||||
"""Narrow `client.server_info` for `connect_with_session`: these servers all identify themselves."""
|
||||
assert client.server_info is not None
|
||||
return client.server_info
|
||||
|
||||
|
||||
async def test_both_servers_call_their_tool_search() -> None:
|
||||
"""tutorial001 + tutorial002: two unrelated servers, one colliding tool name."""
|
||||
async with Client(tutorial001.mcp) as library, Client(tutorial002.mcp) as web:
|
||||
|
|
@ -29,7 +35,7 @@ async def test_a_connected_server_is_aggregated_into_the_group() -> None:
|
|||
"""tutorial003: the group exposes every component of every connected server as a dict."""
|
||||
async with Client(tutorial001.mcp) as library:
|
||||
group = ClientSessionGroup()
|
||||
await group.connect_with_session(library.server_info, library.session)
|
||||
await group.connect_with_session(_server_info(library), library.session)
|
||||
assert sorted(group.tools) == ["search"]
|
||||
assert sorted(group.resources) == ["hours"]
|
||||
assert group.prompts == {}
|
||||
|
|
@ -40,9 +46,9 @@ async def test_colliding_names_are_rejected() -> None:
|
|||
"""tutorial003: without a hook the second `search` raises, and nothing from `Web` is kept."""
|
||||
async with Client(tutorial001.mcp) as library, Client(tutorial002.mcp) as web:
|
||||
group = ClientSessionGroup()
|
||||
await group.connect_with_session(library.server_info, library.session)
|
||||
await group.connect_with_session(_server_info(library), library.session)
|
||||
with pytest.raises(MCPError) as exc_info:
|
||||
await group.connect_with_session(web.server_info, web.session)
|
||||
await group.connect_with_session(_server_info(web), web.session)
|
||||
assert str(exc_info.value) == "{'search'} already exist in group tools."
|
||||
assert exc_info.value.error.code == INVALID_PARAMS
|
||||
assert sorted(group.tools) == ["search"]
|
||||
|
|
@ -56,8 +62,8 @@ async def test_component_name_hook_prefixes_every_name() -> None:
|
|||
"""tutorial004: the hook rewrites every registered name, so both servers coexist."""
|
||||
async with Client(tutorial001.mcp) as library, Client(tutorial002.mcp) as web:
|
||||
group = ClientSessionGroup(component_name_hook=tutorial004.by_server)
|
||||
await group.connect_with_session(library.server_info, library.session)
|
||||
await group.connect_with_session(web.server_info, web.session)
|
||||
await group.connect_with_session(_server_info(library), library.session)
|
||||
await group.connect_with_session(_server_info(web), web.session)
|
||||
assert sorted(group.tools) == ["Library.search", "Web.search"]
|
||||
assert sorted(group.resources) == ["Library.hours"]
|
||||
|
||||
|
|
@ -71,7 +77,7 @@ async def test_the_key_is_prefixed_but_the_wire_name_is_not() -> None:
|
|||
"""tutorial004: the dict key is yours; the `Tool` inside keeps the name the server declared."""
|
||||
async with Client(tutorial002.mcp) as web:
|
||||
group = ClientSessionGroup(component_name_hook=tutorial004.by_server)
|
||||
await group.connect_with_session(web.server_info, web.session)
|
||||
await group.connect_with_session(_server_info(web), web.session)
|
||||
assert group.tools["Web.search"].name == "search"
|
||||
|
||||
|
||||
|
|
@ -79,8 +85,8 @@ async def test_call_tool_routes_to_the_owning_server() -> None:
|
|||
"""tutorial004: `group.call_tool` resolves the prefixed name to the session that owns it."""
|
||||
async with Client(tutorial001.mcp) as library, Client(tutorial002.mcp) as web:
|
||||
group = ClientSessionGroup(component_name_hook=tutorial004.by_server)
|
||||
await group.connect_with_session(library.server_info, library.session)
|
||||
await group.connect_with_session(web.server_info, web.session)
|
||||
await group.connect_with_session(_server_info(library), library.session)
|
||||
await group.connect_with_session(_server_info(web), web.session)
|
||||
web_result = await group.call_tool("Web.search", {"query": "model context protocol"})
|
||||
assert web_result.structured_content == {"result": "12 pages match 'model context protocol'."}
|
||||
library_result = await group.call_tool("Library.search", {"query": "dune"})
|
||||
|
|
@ -91,8 +97,8 @@ async def test_disconnect_removes_every_component_of_that_server() -> None:
|
|||
"""tutorial004: `disconnect_from_server` takes the session back out of all three dicts."""
|
||||
async with Client(tutorial001.mcp) as library, Client(tutorial002.mcp) as web:
|
||||
group = ClientSessionGroup(component_name_hook=tutorial004.by_server)
|
||||
await group.connect_with_session(library.server_info, library.session)
|
||||
web_session = await group.connect_with_session(web.server_info, web.session)
|
||||
await group.connect_with_session(_server_info(library), library.session)
|
||||
web_session = await group.connect_with_session(_server_info(web), web.session)
|
||||
await group.disconnect_from_server(web_session)
|
||||
assert sorted(group.tools) == ["Library.search"]
|
||||
assert sorted(group.resources) == ["Library.hours"]
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from mcp_types import CallToolResult, TextContent
|
|||
|
||||
from docs_src.testing.tutorial001 import mcp
|
||||
from mcp import Client
|
||||
from tests.docs_src._helpers import strip_server_info
|
||||
|
||||
# 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")]
|
||||
|
|
@ -18,6 +19,7 @@ pytestmark = [pytest.mark.anyio, pytest.mark.filterwarnings("error::mcp.MCPDepre
|
|||
async def test_call_add_tool() -> None:
|
||||
async with Client(mcp, raise_exceptions=True) as client:
|
||||
result = await client.call_tool("add", {"a": 1, "b": 2})
|
||||
result = strip_server_info(result, mcp)
|
||||
assert result == snapshot(
|
||||
CallToolResult(content=[TextContent(type="text", text="3")], structured_content={"result": 3})
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue