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
8 changes: 5 additions & 3 deletions packages/toolbox-core/src/toolbox_core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,11 @@ async def load_toolset(
)
tools.append(tool)

# Track usage for both modes. Skipping this under strict=True left
# overall-used sets empty and made the final toolset check always fail.
overall_used_auth_keys.update(used_auth_keys)
overall_used_bound_params.update(used_bound_keys)

if strict:
validate_unused_requirements(
provided_auth_keys,
Expand All @@ -494,9 +499,6 @@ async def load_toolset(
tool_name,
is_toolset=False,
)
else:
overall_used_auth_keys.update(used_auth_keys)
overall_used_bound_params.update(used_bound_keys)

validate_unused_requirements(
provided_auth_keys,
Expand Down
45 changes: 45 additions & 0 deletions packages/toolbox-core/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,28 @@ async def test_load_tool_with_unused_bound_param_fail(
TOOL_NAME, bound_params={"unused_param": "some_value"}
)

@pytest.mark.asyncio
async def test_load_toolset_strict_with_fully_used_bound_param_success(
self, mock_transport, tool_schema_with_param_P
):
"""Tests that load_toolset succeeds in strict mode when every tool uses all bound params."""
TOOL_P = "tool_with_p"
manifest = ManifestSchema(
serverVersion="0.0.0",
tools={TOOL_P: tool_schema_with_param_P},
)
mock_transport.tools_list_mock.return_value = manifest

async with ToolboxClient(TEST_BASE_URL) as client:
client._ToolboxClient__transport = mock_transport
tools = await client.load_toolset(
bound_params={"param_P": "some_value"}, strict=True
)

assert len(tools) == 1
assert tools[0].__name__ == TOOL_P
assert "param_P" not in tools[0].__signature__.parameters

@pytest.mark.asyncio
async def test_load_toolset_strict_with_partially_used_bound_param_fail(
self, mock_transport, tool_schema_with_param_P, tool_schema_minimal
Expand Down Expand Up @@ -559,6 +581,29 @@ async def test_load_toolset_non_strict_with_unused_bound_param_fail(
TOOLSET_NAME, bound_params={"param_Z": "some_value"}
)

@pytest.mark.asyncio
async def test_load_toolset_strict_with_fully_used_auth_success(
self, mock_transport, tool_schema_requires_auth_X
):
"""Tests that load_toolset succeeds in strict mode when every tool uses all auth tokens."""
TOOL_AUTH = "tool_with_auth"
manifest = ManifestSchema(
serverVersion="0.0.0",
tools={TOOL_AUTH: tool_schema_requires_auth_X},
)
mock_transport.tools_list_mock.return_value = manifest

async with ToolboxClient(TEST_BASE_URL) as client:
client._ToolboxClient__transport = mock_transport
tools = await client.load_toolset(
auth_token_getters={"auth_service_X": lambda: "token"}, strict=True
)

assert len(tools) == 1
assert tools[0].__name__ == TOOL_AUTH
assert tools[0]._required_authn_params == {}
assert tools[0]._required_authz_tokens == ()

@pytest.mark.asyncio
async def test_load_toolset_strict_with_partially_used_auth_fail(
self, mock_transport, tool_schema_requires_auth_X, tool_schema_minimal
Expand Down
26 changes: 26 additions & 0 deletions packages/toolbox-core/tests/test_sync_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,32 @@ def test_sync_load_toolset_success(
assert result1 == f"{TOOL1_NAME}_ok"


def test_sync_load_toolset_strict_with_fully_used_bound_param_success(
sync_client, mock_transport
):
"""Sync path: strict=True succeeds when every tool uses all bound params."""
TOOL_P = "tool_with_p"
schema = ToolSchema(
description="Tool with Parameter P",
parameters=[
ParameterSchema(name="param_P", type="string", description="Parameter P"),
],
)
manifest = ManifestSchema(serverVersion="0.0.0", tools={TOOL_P: schema})
mock_transport.tools_list_mock.return_value = manifest
sync_client._ToolboxSyncClient__async_client._ToolboxClient__transport = (
mock_transport
)

tools = sync_client.load_toolset(
bound_params={"param_P": "some_value"}, strict=True
)

assert len(tools) == 1
assert tools[0].__name__ == TOOL_P
assert "param_P" not in tools[0].__signature__.parameters


def test_sync_invoke_tool_server_error(
test_tool_str_schema, sync_client, mock_transport
):
Expand Down
Loading