python-sdk/tests/interaction/lowlevel/test_meta.py

63 lines
2.7 KiB
Python

"""Request and result _meta round trips against the low-level Server, through the public Client API.
Meta is opaque pass-through data, so these tests assert identity against the value that was sent
rather than snapshotting a literal: the expected value and the sent value are the same variable,
which also proves the SDK injected nothing alongside it.
"""
import pytest
from mcp import types
from mcp.server import Server, ServerRequestContext
from mcp.types import CallToolResult, RequestParamsMeta, TextContent
from tests.interaction._connect import Connect
from tests.interaction._requirements import requirement
pytestmark = pytest.mark.anyio
@requirement("meta:request-to-handler")
async def test_request_meta_reaches_handler(connect: Connect) -> None:
"""The _meta object the client attaches to a request arrives at the tool handler unchanged."""
request_meta: RequestParamsMeta = {"example.com/trace": "abc-123"}
observed_metas: list[dict[str, object]] = []
async def list_tools(
ctx: ServerRequestContext, params: types.PaginatedRequestParams | None
) -> types.ListToolsResult:
return types.ListToolsResult(tools=[types.Tool(name="traced", input_schema={"type": "object"})])
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "traced"
assert ctx.meta is not None
observed_metas.append(dict(ctx.meta))
return CallToolResult(content=[TextContent(text="traced")])
server = Server("observability", on_list_tools=list_tools, on_call_tool=call_tool)
async with connect(server) as client:
await client.call_tool("traced", {}, meta=request_meta)
assert observed_metas == [dict(request_meta)]
@requirement("meta:result-to-client")
async def test_result_meta_reaches_client(connect: Connect) -> None:
"""The _meta object a handler attaches to its result is delivered to the client unchanged."""
result_meta = {"example.com/cost": 3}
async def list_tools(
ctx: ServerRequestContext, params: types.PaginatedRequestParams | None
) -> types.ListToolsResult:
return types.ListToolsResult(tools=[types.Tool(name="metered", input_schema={"type": "object"})])
async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestParams) -> CallToolResult:
assert params.name == "metered"
return CallToolResult(content=[TextContent(text="done")], _meta=result_meta)
server = Server("observability", on_list_tools=list_tools, on_call_tool=call_tool)
async with connect(server) as client:
result = await client.call_tool("metered", {})
assert result == CallToolResult(content=[TextContent(text="done")], _meta=result_meta)