Skip to content

Commit 9d7b828

Browse files
authored
Merge pull request #55 from georgeatparallel/parallel-search-6e308ef1-2475-4e87-af13-b1a8a98d627d
Add an opt-in Parallel Search MCP provider
2 parents a3c1be3 + edc0847 commit 9d7b828

11 files changed

Lines changed: 881 additions & 6 deletions

File tree

β€Ž.env.exampleβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ OFFICEQA_DOC_MODE=parsed
2121
FRONTIER_AGENT_DATASETS_DIR=
2222

2323
# Optional: web search and page fetching.
24+
# Serper remains the default. Set WEB_SEARCH_PROVIDER=parallel to use the free,
25+
# keyless Parallel Search MCP for web_search (up to 10 results per query);
26+
# this does not change web_fetch.
27+
WEB_SEARCH_PROVIDER=serper
2428
# Support any Serper.dev-compatible endpoint (like litescrape.com, serpbase.dev,
2529
# and others): set SERPER_BASE_URL and a provider-issued SERPER_API_KEY.
2630
SERPER_API_KEY=

β€ŽREADME.mdβ€Ž

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ OPENAI_BASE_URL=https://your-openai-compatible-endpoint/v1
149149
OPENAI_MODEL=your-model-name
150150
151151
# Optional web research tools
152+
WEB_SEARCH_PROVIDER=serper
152153
SERPER_API_KEY=
153154
SERPER_BASE_URL=https://google.serper.dev
154155
JINA_API_KEY=
@@ -157,6 +158,14 @@ JINA_API_KEY=
157158
Support any Serper.dev-compatible endpoint (like litescrape.com, serpbase.dev,
158159
and others) by setting `SERPER_BASE_URL` and a provider-issued `SERPER_API_KEY`.
159160

161+
Set `WEB_SEARCH_PROVIDER=parallel` to use the free, keyless
162+
[Parallel Search MCP](https://docs.parallel.ai/integrations/mcp/search-mcp) for
163+
`web_search`. Serper remains the default, so existing keys and missing-key
164+
errors keep their current behavior. Parallel MCP does not support the tool's
165+
custom region, language, or time filters, or counts above 10 per query; choose
166+
Serper when those are needed.
167+
`web_fetch` continues to use its existing fetch provider.
168+
160169
Start the TUI:
161170

162171
```bash

β€Žapodex/README.mdβ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ cp .env.example .env
8888
# OPENAI_API_KEY=...
8989
# OPENAI_BASE_URL=https://api.openai.com/v1
9090
# OPENAI_MODEL=gpt-4o
91-
# research mode additionally wants SERPER_API_KEY / JINA_API_KEY
91+
# For keyless Parallel search set WEB_SEARCH_PROVIDER=parallel; it returns up
92+
# to 10 results per query. Serper remains the default and uses SERPER_API_KEY.
93+
# Page fetch uses JINA_API_KEY.
9294

9395
# Interactive TUI, Stateful ReAct, against another repository
9496
frontier-agent --mode react --cwd /path/to/your/repo

β€Žapodex/config.pyβ€Ž

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,18 @@ def inspect_runtime_config(
222222
mode=active_mode,
223223
env=env,
224224
)
225-
if "web_search" in tool_names and not _configured(env.get("SERPER_API_KEY")):
225+
search_provider = (env.get("WEB_SEARCH_PROVIDER") or "serper").strip().lower()
226+
if "web_search" in tool_names and search_provider not in {"serper", "parallel"}:
227+
issues.append(RuntimeConfigIssue(
228+
code="invalid_web_search_provider",
229+
message="WEB_SEARCH_PROVIDER must be set to 'serper' or 'parallel'.",
230+
env_var="WEB_SEARCH_PROVIDER",
231+
))
232+
if (
233+
"web_search" in tool_names
234+
and search_provider == "serper"
235+
and not _configured(env.get("SERPER_API_KEY"))
236+
):
226237
issues.append(RuntimeConfigIssue(
227238
code="missing_serper_api_key",
228239
message=(

β€Žapodex/tests/test_config_preflight.pyβ€Ž

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,22 @@ def test_search_credentials_are_checked_when_the_profile_binds_the_web_tools():
111111
assert with_search.ok
112112
assert [issue.code for issue in with_search.warnings] == ["missing_jina_api_key"]
113113

114+
parallel = inspect_runtime_config(
115+
cfg,
116+
profile=_profile(tool_names=_WEB_TOOLS),
117+
environ={"WEB_SEARCH_PROVIDER": "parallel"},
118+
)
119+
assert parallel.ok
120+
assert [issue.code for issue in parallel.warnings] == ["missing_jina_api_key"]
121+
122+
invalid_provider = inspect_runtime_config(
123+
cfg,
124+
profile=_profile(tool_names=_WEB_TOOLS),
125+
environ={"WEB_SEARCH_PROVIDER": "unknown"},
126+
)
127+
assert not invalid_provider.ok
128+
assert "invalid_web_search_provider" in {issue.code for issue in invalid_provider.errors}
129+
114130
no_web_tools = inspect_runtime_config(
115131
cfg, profile=_profile(tool_names=("bash", "read_file")), environ={},
116132
)

β€Žapodex/tests/test_userenv.pyβ€Ž

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,11 @@ def test_resolution_never_carries_a_value(launch, user_file) -> None:
322322

323323

324324
def test_forwarded_names_are_names_only_and_only_when_set(launch, user_file, monkeypatch) -> None:
325-
_write(user_file, f"OPENAI_API_KEY={_SECRET}\nCUSTOM_PROVIDER_TOKEN=abc\n")
325+
_write(
326+
user_file,
327+
f"OPENAI_API_KEY={_SECRET}\nCUSTOM_PROVIDER_TOKEN=abc\n"
328+
"WEB_SEARCH_PROVIDER=parallel\n",
329+
)
326330
monkeypatch.setenv("SERPER_API_KEY", "serper-secret")
327331
monkeypatch.delenv("JINA_API_KEY", raising=False)
328332

@@ -331,19 +335,22 @@ def test_forwarded_names_are_names_only_and_only_when_set(launch, user_file, mon
331335

332336
assert "OPENAI_API_KEY" in names # from the file
333337
assert "CUSTOM_PROVIDER_TOKEN" in names # file-defined, even if unlisted
338+
assert "WEB_SEARCH_PROVIDER" in names # provider selected in the user file
334339
assert "SERPER_API_KEY" in names # well-known and exported
335340
assert "JINA_API_KEY" not in names # well-known but not set
336341
assert _SECRET not in " ".join(names)
337342

338343

339344
def test_empty_resolution_forwards_only_exported_well_known_names(monkeypatch) -> None:
340345
monkeypatch.setenv("OPENAI_MODEL", "m")
346+
monkeypatch.setenv("WEB_SEARCH_PROVIDER", "parallel")
341347
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
342348

343349
names = EnvResolution.empty().forwarded_names()
344350

345351
assert "OPENAI_MODEL" in names
346352
assert "OPENAI_API_KEY" not in names
353+
assert "WEB_SEARCH_PROVIDER" not in names
347354

348355

349356
# ── the CLI: --cwd, --model, and where .env is looked up ──────────────────

β€Ždocs/install/global-install.mdβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ chmod 600 "$config_dir/env"
7070
```
7171

7272
Optional web tools take `SERPER_API_KEY` and `JINA_API_KEY` in the same file.
73+
Add `WEB_SEARCH_PROVIDER=parallel` to this file to use the keyless Parallel
74+
Search MCP for `web_search`; it supports up to 10 results per query. Serper
75+
remains the default. Exporting the setting also works for native runs.
76+
`web_fetch` keeps its existing provider.
7377
`APODEX_ENV_FILE=/path/to/file` points the CLI at a different file.
7478

7579
Rules for the file:

0 commit comments

Comments
Β (0)