mirror of
https://github.com/HKUDS/OpenSpace.git
synced 2026-10-13 03:58:18 +00:00
Merge e37efc6264 into 38277815ed
This commit is contained in:
commit
527dea89ab
5 changed files with 108 additions and 0 deletions
43
examples/parallel-search/README.md
Normal file
43
examples/parallel-search/README.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Parallel web search and page fetching
|
||||
|
||||
Use [Parallel Search MCP](https://docs.parallel.ai/integrations/mcp/search-mcp)
|
||||
through OpenSpace's existing MCP provider for free web search and page fetching,
|
||||
with no account or API key. Anonymous searches use Fast mode. The free endpoint
|
||||
is rate limited and intended for exploration and light use.
|
||||
|
||||
## Run the example
|
||||
|
||||
From the repository root, with Python 3.12 or newer:
|
||||
|
||||
```bash
|
||||
python -m venv .venv-parallel
|
||||
source .venv-parallel/bin/activate
|
||||
cd examples/parallel-search
|
||||
python -m pip install -r requirements.txt
|
||||
OPENSPACE_SKIP_DOTENV=1 python search.py
|
||||
```
|
||||
|
||||
On Windows, activate with `.venv-parallel\Scripts\activate` and set
|
||||
`OPENSPACE_SKIP_DOTENV=1` in your shell before running `python search.py`.
|
||||
The example constrains the MCP SDK to 1.x, which the current OpenSpace
|
||||
connectors require.
|
||||
|
||||
The script loads `config_mcp.json`, discovers the live tools through
|
||||
`MCPProvider`, calls `web_search` and `web_fetch`, prints their results, and
|
||||
closes its sessions. It makes direct tool calls without invoking an LLM or
|
||||
requiring LLM credentials. Customize the search and the page to fetch:
|
||||
|
||||
```bash
|
||||
OPENSPACE_SKIP_DOTENV=1 python search.py \
|
||||
--query "Python asyncio TaskGroup cancellation" \
|
||||
--url https://docs.python.org/3/library/asyncio-task.html
|
||||
```
|
||||
|
||||
## Add the server to your OpenSpace configuration
|
||||
|
||||
Merge the `parallel` entry from this example's `mcpServers` into your
|
||||
`openspace/config/config_mcp.json`, retaining any existing entries. OpenSpace's
|
||||
HTTP connector negotiates Streamable HTTP with `https://search.parallel.ai/mcp`;
|
||||
the entry includes an OpenSpace User-Agent and no authentication header.
|
||||
The example does not change the built-in `web_search` provider. Parallel's
|
||||
`web_search` and `web_fetch` are external MCP tools.
|
||||
10
examples/parallel-search/config_mcp.json
Normal file
10
examples/parallel-search/config_mcp.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"mcpServers": {
|
||||
"parallel": {
|
||||
"url": "https://search.parallel.ai/mcp",
|
||||
"headers": {
|
||||
"User-Agent": "OpenSpace/2.0 (+https://github.com/HKUDS/OpenSpace)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
examples/parallel-search/requirements.txt
Normal file
3
examples/parallel-search/requirements.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Install from this directory. OpenSpace's MCP connectors use the 1.x SDK.
|
||||
-e ../..
|
||||
mcp>=1.0.0,<2
|
||||
48
examples/parallel-search/search.py
Normal file
48
examples/parallel-search/search.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
"""Search and fetch through OpenSpace's MCP provider, without an LLM call."""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
from openspace.grounding.backends.mcp import MCPProvider
|
||||
from openspace.grounding.core.types import ToolStatus
|
||||
|
||||
|
||||
async def search_and_fetch(query: str, url: str) -> None:
|
||||
config = json.loads(Path(__file__).with_name("config_mcp.json").read_text())
|
||||
provider = MCPProvider(config)
|
||||
session_id = uuid4().hex
|
||||
try:
|
||||
tools = {tool.name for tool in await provider.list_tools(use_cache=False)}
|
||||
if not {"web_search", "web_fetch"}.issubset(tools):
|
||||
raise RuntimeError("Parallel search and fetch tools are unavailable")
|
||||
|
||||
calls = [
|
||||
("web_search", {"objective": query, "search_queries": [query]}),
|
||||
("web_fetch", {"urls": [url], "objective": query[:200]}),
|
||||
]
|
||||
for name, parameters in calls:
|
||||
result = await provider.call_tool(
|
||||
"mcp-parallel", name, {**parameters, "session_id": session_id}
|
||||
)
|
||||
if result.status != ToolStatus.SUCCESS:
|
||||
raise RuntimeError(f"{name} failed: {result.error or result.content}")
|
||||
if not result.content:
|
||||
raise RuntimeError(f"{name} returned no content")
|
||||
print(f"\n{name}:\n{result.content}")
|
||||
finally:
|
||||
await provider.close_all_sessions()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--query", default="Python asyncio task cancellation")
|
||||
parser.add_argument("--url", default="https://docs.python.org/3/library/asyncio-task.html")
|
||||
args = parser.parse_args()
|
||||
asyncio.run(search_and_fetch(args.query, args.url))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -200,6 +200,10 @@ cp openspace/config/config_mcp.json.example openspace/config/config_mcp.json
|
|||
}
|
||||
```
|
||||
|
||||
For an HTTP MCP server with free, keyless web search and page fetching, see the
|
||||
[runnable Parallel Search example](../../examples/parallel-search/README.md).
|
||||
Merge its server entry into your configuration to keep existing MCP servers.
|
||||
|
||||
## 5. Execution Mode
|
||||
|
||||
The shell backend supports only local execution. This keeps Bash sandbox
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue