Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions wayflowcore/tests/integration/tools/test_servertool_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ def many_params_tool(
param9: Annotated[List[Dict[str, int]], "Param 9 desc"] = [{"a": 1}],
param10: Annotated[Dict[str, List[str]], "Param 10 desc"] = {"b": ["c"]},
param11: Annotated[int | str, "Param 11 desc"] = "uniontype",
param12_union_pipes: Annotated[int | str | float, "Param 12 desc"] = "a",
param13_optional_pipes: Annotated[int | None, "Param 13 desc"] = None,
) -> Annotated[str, "Output description"]:
"""Many parameters tool description"""
return "result"
Expand Down Expand Up @@ -546,6 +548,18 @@ def many_params_tool(
"anyOf": [{"type": "integer"}, {"type": "string"}],
"title": "Param11",
},
"param12_union_pipes": {
'description': 'Param 12 desc',
'anyOf': [{'type': 'integer'}, {'type': 'string'}, {'type': 'number'}],
'default': 'a',
'title': 'Param12 Union Pipes'
},
"param13_optional_pipes": {
'default': None,
'description': 'Param 13 desc',
'title': 'Param13 Optional Pipes',
'type': 'integer'
},
}

_check_tool_correctness(
Expand Down
25 changes: 24 additions & 1 deletion wayflowcore/tests/mcptools/start_mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import argparse
from contextvars import ContextVar
from os import PathLike
from typing import Annotated, AsyncGenerator, Literal, Optional
from typing import Annotated, AsyncGenerator, Dict, List, Literal, Optional, Union

import anyio
from mcp.server.fastmcp import Context
Expand Down Expand Up @@ -128,6 +128,29 @@ def zbuk_tool(a: int, b: int) -> int:
def ggwp_tool(a: int, b: int) -> int:
return a + b // 2

@server.tool(
description="This tool is not useful."
)
def all_input_types_tool(
# basic types
a: int,
b: float,
c: str,
d: bool,
# complext types
e: List[int],
f: list[bool],
g: Dict[str, int],
h: dict[str, int],
i: Optional[str],
j: int | None,
k: Union[str, int, float],
l: str | int | float,
# complex compositions
m: List[Dict[str | float, Optional[int]]]
) -> float:
return a + b / 2

@server.tool(description="Tool to return a random string")
def generate_random_string() -> str:
import random
Expand Down
49 changes: 46 additions & 3 deletions wayflowcore/tests/mcptools/test_mcp_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
AnyProperty,
BooleanProperty,
DictProperty,
FloatProperty,
IntegerProperty,
ListProperty,
NullProperty,
Expand Down Expand Up @@ -128,15 +129,57 @@ def streamablehttp_client_transport_mtls(
ssl_ca_cert=ca_cert_path,
)


def run_toolbox_test(transport: ClientTransport) -> None:
toolbox = MCPToolBox(client_transport=transport)
tools = toolbox.get_tools() # need
assert len(tools) == 17
assert len(tools) == 18

# check some tool signatures
mcp_tool = next(t for t in tools if t.name == "fooza_tool")
assert mcp_tool.run(a=1, b=2) == "7"
assert mcp_tool.input_descriptors == [IntegerProperty(name="a"), IntegerProperty(name="b")]

all_input_types_tool = next(t for t in tools if t.name == "all_input_types_tool")

expected_inputs = [
# basic types
IntegerProperty(name="a"),
FloatProperty(name="b"),
StringProperty(name="c"),
BooleanProperty(name="d"),
# complex types
ListProperty(name="e", item_type = IntegerProperty()),
ListProperty(name="f", item_type = BooleanProperty()),
# Ideally it would be:
# DictProperty(name="g", key_type = StringProperty(), value_type = IntegerProperty()),
# DictProperty(name="h", key_type = StringProperty(), value_type = IntegerProperty()),
# But key types are not provided by the MCP servers:
DictProperty(name="g", key_type = AnyProperty(), value_type = IntegerProperty()),
DictProperty(name="h", key_type = AnyProperty(), value_type = IntegerProperty()),
UnionProperty(name="i", any_of = [StringProperty(), NullProperty()]),
UnionProperty(name="j", any_of = [IntegerProperty(), NullProperty()]),
UnionProperty(name="k", any_of = [StringProperty(), IntegerProperty(), FloatProperty()]),
UnionProperty(name="l", any_of = [StringProperty(), IntegerProperty(), FloatProperty()]),

# Ideally it would be:
# ListProperty(
# name="m",
# item_type=DictProperty(
# key_type=UnionProperty(any_of=[StringProperty(), FloatProperty()]),
# value_type=UnionProperty(any_of=[IntegerProperty(), NullProperty()])
# )
# ),
# But MCP doesn't pass the key type for dictionaries
ListProperty(
name="m",
item_type=DictProperty(
key_type=AnyProperty(),
value_type=UnionProperty(any_of=[IntegerProperty(), NullProperty()])
)
),
]
assert all_input_types_tool.input_descriptors == expected_inputs


@pytest.mark.parametrize(
"client_transport_name",
Expand Down Expand Up @@ -313,7 +356,7 @@ async def test_mcp_toolboxes_from_different_servers_do_not_conflict_in_same_agen
# We call this method to actively retrieve tools from the lazy toolboxes
all_agent_tools = await AgentConversationExecutor._collect_tools(config=agent, curr_iter=0)
# 17 from toolbox 1, 2 from toolbox 2
assert len(all_agent_tools) == 17 + 2
assert len(all_agent_tools) == 18 + 2
# Ensure that one tool from the first toolbox and one from the second are in the list
tool_names = set(tool.name for tool in all_agent_tools)
assert all(tool_name in tool_names for tool_name in ["fooza_tool", "alt_mul_tool"])
Expand Down