Expose RequestParams._meta in ClientSession.call_tool (#1231)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
This commit is contained in:
parent
c733eec5d9
commit
40acbc596c
2 changed files with 129 additions and 7 deletions
|
|
@ -328,16 +328,19 @@ class ClientSession(
|
|||
arguments: dict[str, Any] | None = None,
|
||||
read_timeout_seconds: timedelta | None = None,
|
||||
progress_callback: ProgressFnT | None = None,
|
||||
*,
|
||||
meta: dict[str, Any] | None = None,
|
||||
) -> types.CallToolResult:
|
||||
"""Send a tools/call request with optional progress callback support."""
|
||||
|
||||
_meta: types.RequestParams.Meta | None = None
|
||||
if meta is not None:
|
||||
_meta = types.RequestParams.Meta(**meta)
|
||||
|
||||
result = await self.send_request(
|
||||
types.ClientRequest(
|
||||
types.CallToolRequest(
|
||||
params=types.CallToolRequestParams(
|
||||
name=name,
|
||||
arguments=arguments,
|
||||
),
|
||||
params=types.CallToolRequestParams(name=name, arguments=arguments, _meta=_meta),
|
||||
)
|
||||
),
|
||||
types.CallToolResult,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from mcp.shared.session import RequestResponder
|
|||
from mcp.shared.version import SUPPORTED_PROTOCOL_VERSIONS
|
||||
from mcp.types import (
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
CallToolResult,
|
||||
ClientNotification,
|
||||
ClientRequest,
|
||||
Implementation,
|
||||
|
|
@ -23,6 +24,7 @@ from mcp.types import (
|
|||
JSONRPCResponse,
|
||||
ServerCapabilities,
|
||||
ServerResult,
|
||||
TextContent,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -492,8 +494,125 @@ async def test_client_capabilities_with_custom_callbacks():
|
|||
|
||||
# Assert that capabilities are properly set with custom callbacks
|
||||
assert received_capabilities is not None
|
||||
assert received_capabilities.sampling is not None # Custom sampling callback provided
|
||||
# Custom sampling callback provided
|
||||
assert received_capabilities.sampling is not None
|
||||
assert isinstance(received_capabilities.sampling, types.SamplingCapability)
|
||||
assert received_capabilities.roots is not None # Custom list_roots callback provided
|
||||
# Custom list_roots callback provided
|
||||
assert received_capabilities.roots is not None
|
||||
assert isinstance(received_capabilities.roots, types.RootsCapability)
|
||||
assert received_capabilities.roots.listChanged is True # Should be True for custom callback
|
||||
# Should be True for custom callback
|
||||
assert received_capabilities.roots.listChanged is True
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@pytest.mark.parametrize(argnames="meta", argvalues=[None, {"toolMeta": "value"}])
|
||||
async def test_client_tool_call_with_meta(meta: dict[str, Any] | None):
|
||||
"""Test that client tool call requests can include metadata"""
|
||||
client_to_server_send, client_to_server_receive = anyio.create_memory_object_stream[SessionMessage](1)
|
||||
server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[SessionMessage](1)
|
||||
|
||||
mocked_tool = types.Tool(name="sample_tool", inputSchema={})
|
||||
|
||||
async def mock_server():
|
||||
# Receive initialization request from client
|
||||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request.root, JSONRPCRequest)
|
||||
request = ClientRequest.model_validate(
|
||||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
# Answer initialization request
|
||||
await server_to_client_send.send(
|
||||
SessionMessage(
|
||||
JSONRPCMessage(
|
||||
JSONRPCResponse(
|
||||
jsonrpc="2.0",
|
||||
id=jsonrpc_request.root.id,
|
||||
result=result.model_dump(by_alias=True, mode="json", exclude_none=True),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Receive initialized notification
|
||||
await client_to_server_receive.receive()
|
||||
|
||||
# Wait for the client to send a 'tools/call' request
|
||||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request.root, JSONRPCRequest)
|
||||
|
||||
assert jsonrpc_request.root.method == "tools/call"
|
||||
|
||||
if meta is not None:
|
||||
assert jsonrpc_request.root.params
|
||||
assert "_meta" in jsonrpc_request.root.params
|
||||
assert jsonrpc_request.root.params["_meta"] == meta
|
||||
|
||||
result = ServerResult(
|
||||
CallToolResult(content=[TextContent(type="text", text="Called successfully")], isError=False)
|
||||
)
|
||||
|
||||
# Send the tools/call result
|
||||
await server_to_client_send.send(
|
||||
SessionMessage(
|
||||
JSONRPCMessage(
|
||||
JSONRPCResponse(
|
||||
jsonrpc="2.0",
|
||||
id=jsonrpc_request.root.id,
|
||||
result=result.model_dump(by_alias=True, mode="json", exclude_none=True),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Wait for the tools/list request from the client
|
||||
# The client requires this step to validate the tool output schema
|
||||
session_message = await client_to_server_receive.receive()
|
||||
jsonrpc_request = session_message.message
|
||||
assert isinstance(jsonrpc_request.root, JSONRPCRequest)
|
||||
|
||||
assert jsonrpc_request.root.method == "tools/list"
|
||||
|
||||
result = types.ListToolsResult(tools=[mocked_tool])
|
||||
|
||||
await server_to_client_send.send(
|
||||
SessionMessage(
|
||||
JSONRPCMessage(
|
||||
JSONRPCResponse(
|
||||
jsonrpc="2.0",
|
||||
id=jsonrpc_request.root.id,
|
||||
result=result.model_dump(by_alias=True, mode="json", exclude_none=True),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
server_to_client_send.close()
|
||||
|
||||
async with (
|
||||
ClientSession(
|
||||
server_to_client_receive,
|
||||
client_to_server_send,
|
||||
) as session,
|
||||
anyio.create_task_group() as tg,
|
||||
client_to_server_send,
|
||||
client_to_server_receive,
|
||||
server_to_client_send,
|
||||
server_to_client_receive,
|
||||
):
|
||||
tg.start_soon(mock_server)
|
||||
|
||||
await session.initialize()
|
||||
|
||||
await session.call_tool(name=mocked_tool.name, arguments={"foo": "bar"}, meta=meta)
|
||||
|
|
|
|||
Loading…
Reference in a new issue