mirror of
https://github.com/usestrix/strix.git
synced 2026-09-08 22:21:05 +00:00
docs, tests: add Parallel Search MCP as optional web search setup (#1179)
This commit is contained in:
parent
717ffc8f4c
commit
7711d68fdc
2 changed files with 84 additions and 1 deletions
|
|
@ -8,6 +8,7 @@ Strix can connect to [Model Context Protocol (MCP)](https://modelcontextprotocol
|
|||
A few things it pays off for:
|
||||
|
||||
- **A database server.** The agent can read the schema and access policies and see tables left readable without them, rather than guessing from responses.
|
||||
- **A web search / research server (e.g. [Parallel Search](https://search.parallel.ai/mcp)).** The agent can look up current CVEs, security advisories, and documentation in real time without needing a Perplexity API key.
|
||||
- **A hosting or infrastructure server.** Deployments, domains and environment variable names tell it what is really running, so it tests what exists instead of what it discovered by crawling.
|
||||
- **An issue tracker.** Known and accepted risks stop the agent re-reporting findings you already triaged.
|
||||
- **A logging server.** Reading logs lets it confirm an exploit attempt actually landed instead of inferring it from a status code.
|
||||
|
|
@ -22,7 +23,7 @@ Create the directory if it does not exist, then write the file:
|
|||
mkdir -p ~/.strix
|
||||
```
|
||||
|
||||
Paste the servers you want into `~/.strix/mcp-servers.json`. The example below shows one of each transport: a local filesystem server over `stdio` and a remote GitHub server over `http` with a bearer token:
|
||||
Paste the servers you want into `~/.strix/mcp-servers.json`. The example below shows a local filesystem server over `stdio`, a keyless remote web search server (Parallel Search) over `http`, and a remote GitHub server with a bearer token:
|
||||
|
||||
```json
|
||||
[
|
||||
|
|
@ -32,6 +33,12 @@ Paste the servers you want into `~/.strix/mcp-servers.json`. The example below s
|
|||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"]
|
||||
},
|
||||
{
|
||||
"name": "parallel_search",
|
||||
"transport": "http",
|
||||
"url": "https://search.parallel.ai/mcp",
|
||||
"notes": "Web search for CVEs, advisories, and documentation via Parallel Search"
|
||||
},
|
||||
{
|
||||
"name": "github",
|
||||
"transport": "http",
|
||||
|
|
|
|||
|
|
@ -754,6 +754,36 @@ def test_loader_skips_bad_entry_but_keeps_good_ones(tmp_path: Path) -> None:
|
|||
assert [c.name for c in configs] == ["local_fs"]
|
||||
|
||||
|
||||
def test_loader_parses_parallel_search_http_config(tmp_path: Path) -> None:
|
||||
config_file = tmp_path / "mcp-servers.json"
|
||||
config_file.write_text(
|
||||
json.dumps(
|
||||
[
|
||||
{
|
||||
"name": "parallel_search",
|
||||
"transport": "http",
|
||||
"url": "https://search.parallel.ai/mcp",
|
||||
"notes": (
|
||||
"Web search for CVEs, advisories, and documentation via Parallel Search"
|
||||
),
|
||||
}
|
||||
]
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
configs = load_user_mcp_configs(config_file)
|
||||
|
||||
assert len(configs) == 1
|
||||
assert configs[0].name == "parallel_search"
|
||||
assert configs[0].transport == "http"
|
||||
assert configs[0].url == "https://search.parallel.ai/mcp"
|
||||
assert configs[0].auth is None
|
||||
assert configs[0].notes == (
|
||||
"Web search for CVEs, advisories, and documentation via Parallel Search"
|
||||
)
|
||||
|
||||
|
||||
def test_loader_returns_empty_when_file_absent(tmp_path: Path) -> None:
|
||||
assert load_user_mcp_configs(tmp_path / "does-not-exist.json") == []
|
||||
|
||||
|
|
@ -1599,3 +1629,49 @@ def test_registry_statuses_report_the_live_dead_flag_and_provider() -> None:
|
|||
assert statuses["a"].provider == "supabase"
|
||||
assert statuses["b"].dead is True
|
||||
assert statuses["b"].provider is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_parallel_search_mcp_discovery_and_invocation() -> None:
|
||||
search_tool = MCPTool(
|
||||
name="search",
|
||||
description="Search the web for cybersecurity advisories, CVEs, and documentation",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {"query": {"type": "string"}},
|
||||
"required": ["query"],
|
||||
},
|
||||
)
|
||||
server = FakeMCPServer("parallel_search", [search_tool])
|
||||
session = SupervisedMcpSession.adopt(server, name="parallel_search")
|
||||
registry = McpRegistry()
|
||||
registry.add(
|
||||
name="parallel_search",
|
||||
session=session,
|
||||
tool_count=1,
|
||||
purpose="Web search for CVEs, advisories, and documentation via Parallel Search",
|
||||
)
|
||||
|
||||
listed = await list_mcps.on_invoke_tool(_ctx(registry), "{}")
|
||||
assert any(conn["name"] == "parallel_search" for conn in listed["connections"])
|
||||
|
||||
described = await describe_mcp.on_invoke_tool(
|
||||
_ctx(registry), json.dumps({"connection": "parallel_search"})
|
||||
)
|
||||
assert "MCP connection 'parallel_search' offers 1 tool(s):" in described
|
||||
assert "search: Search the web" in described
|
||||
|
||||
out = await call_mcp.on_invoke_tool(
|
||||
_ctx(registry),
|
||||
json.dumps(
|
||||
{
|
||||
"connection": "parallel_search",
|
||||
"tool": "search",
|
||||
"arguments": {"query": "CVE-2024-1234 exploit PoC"},
|
||||
}
|
||||
),
|
||||
)
|
||||
assert out == {"type": "text", "text": "routed:search"}
|
||||
assert server.calls == [("search", {"query": "CVE-2024-1234 exploit PoC"})]
|
||||
|
||||
await session.aclose()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue