optional search + config validation

This commit is contained in:
lda
2026-04-30 05:58:59 +07:00 Verified
parent fc76d6f8fd
commit ef2f179110
8 changed files with 292 additions and 9 deletions
+67
View File
@@ -3,7 +3,11 @@ from __future__ import annotations
import json
from pathlib import Path
import pytest
from pydantic import ValidationError
from wf_mcp.cli import build_parser, main
from wf_mcp.broker_server import load_broker_config
from tests.test_wf_mcp_support import local_temp_root
@@ -37,6 +41,7 @@ def test_build_parser_accepts_serve_transport() -> None:
assert args.mode == "proxy"
assert args.resources_as_tools is False
assert args.prompts_as_tools is False
assert args.search_tools is False
def test_build_parser_accepts_proxy_compatibility_flags() -> None:
@@ -48,6 +53,7 @@ def test_build_parser_accepts_proxy_compatibility_flags() -> None:
"serve",
"--resources-as-tools",
"--prompts-as-tools",
"--search-tools",
]
)
@@ -55,6 +61,7 @@ def test_build_parser_accepts_proxy_compatibility_flags() -> None:
assert args.mode == "proxy"
assert args.resources_as_tools is True
assert args.prompts_as_tools is True
assert args.search_tools is True
def test_cli_connections_prints_configured_connections(capsys) -> None:
@@ -114,3 +121,63 @@ def test_cli_status_prints_connection_statuses(capsys) -> None:
"prompt_count": 0,
}
]
def test_load_broker_config_normalizes_typed_stdio_metadata() -> None:
tmp_path = local_temp_root() / "cli_typed_stdio_config_test"
tmp_path.mkdir(parents=True, exist_ok=True)
config_path = tmp_path / "wf_mcp.config.json"
config_path.write_text(
json.dumps(
{
"store_root": ".wf_mcp_store",
"connections": [
{
"id": "demo.personal",
"server": "demo",
"account": "personal",
"metadata": {
"command": "python",
"args": ["server.py"],
"env": {"TOKEN": "secret"},
},
}
],
}
),
encoding="utf-8",
)
config = load_broker_config(config_path)
assert config.store_root == (tmp_path / ".wf_mcp_store").resolve()
assert config.connections[0].metadata == {
"transport": "stdio",
"command": "python",
"args": ["server.py"],
"env": {"TOKEN": "secret"},
}
def test_load_broker_config_rejects_bad_metadata_shape() -> None:
tmp_path = local_temp_root() / "cli_bad_config_test"
tmp_path.mkdir(parents=True, exist_ok=True)
config_path = tmp_path / "wf_mcp.config.json"
config_path.write_text(
json.dumps(
{
"connections": [
{
"id": "demo.personal",
"server": "demo",
"account": "personal",
"metadata": {"transport": "stdio", "args": "server.py"},
}
],
}
),
encoding="utf-8",
)
with pytest.raises(ValidationError):
load_broker_config(config_path)
+62
View File
@@ -38,8 +38,27 @@ def test_transparent_proxy_lists_and_calls_upstream_tools() -> None:
async with client:
tools = await client.list_tools()
names = [tool.name for tool in tools]
assert "wf.mcp_list_connections" in names
assert "wf.mcp_get_connection_statuses" in names
assert "fixture.personal_echo_tool" in names
connections_result = await client.call_tool("wf.mcp_list_connections")
assert connections_result.structured_content == {
"result": [
{
"id": "fixture.personal",
"server": "fixture",
"account": "personal",
"enabled": True,
"metadata": {
"transport": "stdio",
"command": sys.executable,
"args": [fixture_server_path()],
},
}
]
}
result = await client.call_tool(
"fixture.personal_echo_tool",
{"text": "hello"},
@@ -77,6 +96,12 @@ def test_transparent_proxy_rejects_invalid_connection_config() -> None:
account="http",
metadata={"transport": "http"},
),
ConnectionConfig(
id="wf.mcp",
server="wf",
account="mcp",
metadata={"transport": "stdio", "command": sys.executable},
),
],
)
@@ -89,6 +114,7 @@ def test_transparent_proxy_rejects_invalid_connection_config() -> None:
assert "fixture.personal: unsupported MCP transport 'websocket'" in message
assert "connection id 'bad_scope.personal' must not contain '_'" in message
assert "fixture.http: http transport requires metadata.url" in message
assert "connection id 'wf.mcp' is reserved by wf-mcp" in message
def test_transparent_proxy_can_expose_resources_and_prompts_as_tools() -> None:
@@ -123,3 +149,39 @@ def test_transparent_proxy_can_expose_resources_and_prompts_as_tools() -> None:
assert "get_prompt" in names
asyncio.run(run_proxy())
def test_transparent_proxy_can_collapse_upstream_tools_behind_search() -> None:
config = BrokerConfig(
store_root=local_temp_root() / "transparent_proxy_search_store",
connections=[
ConnectionConfig(
id="fixture.personal",
server="fixture",
account="personal",
metadata={
"transport": "stdio",
"command": sys.executable,
"args": [fixture_server_path()],
},
)
],
)
async def run_proxy() -> None:
client = create_transparent_proxy_client(config, search_tools=True)
async with client:
tools = await client.list_tools()
names = [tool.name for tool in tools]
assert "search_tools" in names
assert "wf.mcp_list_connections" in names
assert "wf.mcp_get_connection_statuses" in names
assert "fixture.personal_echo_tool" not in names
search_result = await client.call_tool(
"search_tools",
{"query": "echo text back"},
)
assert "fixture.personal_echo_tool" in str(search_result)
asyncio.run(run_proxy())