feat(parallel): add free Parallel Search MCP as the zero-config default web_search provider#90849
Conversation
…lt web_search provider Registers two Parallel web_search providers in the parallel plugin: - parallel-free: keyless, always the free hosted Search MCP (search.parallel.ai/mcp); the zero-config default (autoDetectOrder 76) so web_search works with no key. - parallel: the existing paid v1 REST API (requires PARALLEL_API_KEY). Shared query/result normalization lives in parallel-search-normalize.ts (used by both transports); a minimal Streamable-HTTP JSON-RPC client (parallel-mcp-search.runtime.ts) backs the free path. UI brands the tool-call chip 'Parallel Web Search' on the free path via a searchTransport marker; setup default mirrors runtime auto-detect.
…wners parallel-free is a bundled web_search provider, so add it to the doctor's exhaustive BUNDLED_LEGACY_WEB_SEARCH_OWNERS map (owned by the parallel plugin) and the NON_MIGRATED set — it has no legacy tools.web.search.* shape, so this is a no-op for migration, matching paid parallel/tavily. Keeps the registry complete. (Spotted by diffing the earlier local WIP branch.)
…I-label claim
- Frame the two providers as Parallel Search (Free) vs paid Parallel Search;
remove internal 'v1 REST API' wording.
- Remove conversational/overstated phrasing ('out of the box for everyone').
- Remove the 'labeled Parallel Web Search in the UI' claim (only renders in the
Control UI, not the TUI). Scope the searchTransport code comment accordingly.
The label only rendered in the Control UI, never the TUI (a separate renderer via src/agents/tool-display.ts). Extending it would put provider-specific labeling into a shared/core display path, against the plugin-agnostic-core rule. Reverts the Control-UI labelOverride wiring and removes the now-orphaned searchTransport marker from the free provider's result. The result still carries provider: "parallel-free".
|
Codex review: found issues before merge. Reviewed June 6, 2026, 1:49 AM ET / 05:49 UTC. Summary PR surface: Source +614, Tests +428, Docs +37. Total +1079 across 19 files. Reproducibility: yes. for the review blocker: current main returns the first keyless fallback after credentialed providers fail, and this PR puts Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land Do we have a high-confidence way to reproduce the issue? Yes for the review blocker: current main returns the first keyless fallback after credentialed providers fail, and this PR puts Is this the best way to solve the issue? No, not as-is. The provider implementation and MCP session repair are plausible, but the best maintainable path is either opt-in first or an explicit maintainer decision accepting the zero-config hosted-provider default and its upgrade/privacy/quota implications. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 153a2badb056. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +614, Tests +428, Docs +37. Total +1079 across 19 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
…ist contract The free parallel-free provider reused the paid ParallelSearchSchema, whose session_id allows 1000 chars, but the live Search MCP tools/list schema caps session_id at 100. Parameterize normalizeParallelSessionId(value, maxLength); the free path passes 100 (paid keeps 1000) and advertises the tighter bound in its own ParallelFreeSearchSchema. An over-limit caller id is dropped and a fresh in-contract id is minted. Updates tests and docs accordingly.
|
Thank you for the update to open, free, keyless search. |
…lt web_search provider (openclaw#90849) * feat(parallel): add free Parallel Search MCP as the zero-config default web_search provider Registers two Parallel web_search providers in the parallel plugin: - parallel-free: keyless, always the free hosted Search MCP (search.parallel.ai/mcp); the zero-config default (autoDetectOrder 76) so web_search works with no key. - parallel: the existing paid v1 REST API (requires PARALLEL_API_KEY). Shared query/result normalization lives in parallel-search-normalize.ts (used by both transports); a minimal Streamable-HTTP JSON-RPC client (parallel-mcp-search.runtime.ts) backs the free path. UI brands the tool-call chip 'Parallel Web Search' on the free path via a searchTransport marker; setup default mirrors runtime auto-detect. * chore(parallel): register parallel-free in doctor legacy-web-search owners parallel-free is a bundled web_search provider, so add it to the doctor's exhaustive BUNDLED_LEGACY_WEB_SEARCH_OWNERS map (owned by the parallel plugin) and the NON_MIGRATED set — it has no legacy tools.web.search.* shape, so this is a no-op for migration, matching paid parallel/tavily. Keeps the registry complete. (Spotted by diffing the earlier local WIP branch.) * docs(parallel): restore concise frontmatter summary * docs(parallel): clearer, professional copy; drop v1 REST jargon and UI-label claim - Frame the two providers as Parallel Search (Free) vs paid Parallel Search; remove internal 'v1 REST API' wording. - Remove conversational/overstated phrasing ('out of the box for everyone'). - Remove the 'labeled Parallel Web Search in the UI' claim (only renders in the Control UI, not the TUI). Scope the searchTransport code comment accordingly. * revert(parallel): drop the "Parallel Web Search" tool-call branding The label only rendered in the Control UI, never the TUI (a separate renderer via src/agents/tool-display.ts). Extending it would put provider-specific labeling into a shared/core display path, against the plugin-agnostic-core rule. Reverts the Control-UI labelOverride wiring and removes the now-orphaned searchTransport marker from the free provider's result. The result still carries provider: "parallel-free". * fix(parallel): cap free Search MCP session_id at its 100-char tools/list contract The free parallel-free provider reused the paid ParallelSearchSchema, whose session_id allows 1000 chars, but the live Search MCP tools/list schema caps session_id at 100. Parameterize normalizeParallelSessionId(value, maxLength); the free path passes 100 (paid keeps 1000) and advertises the tighter bound in its own ParallelFreeSearchSchema. An over-limit caller id is dropped and a fresh in-contract id is minted. Updates tests and docs accordingly.
Note
AI-assisted PR. Implementation and iterative review were AI-driven (Claude), with
codex review --base origin/mainrun repeatedly until it returned no actionable findings. The author reviewed every change, ran the live integration, and understands the code being merged.Summary
What problem does this PR solve?
OpenClaw had no high-quality, zero-setup, free web search by default. With no API key and no provider configured,
web_searchfell back to DuckDuckGo (labeled "experimental", an unofficial HTML-based integration); high-quality Parallel search required a paidPARALLEL_API_KEY.Why does this matter now?
We want every OpenClaw install to get a high-quality web search experience for free, with no account, key, or setup.
What is the intended outcome?
A new bundled provider, Parallel Search (Free) (
parallel-free), backed by Parallel's free hosted Search MCP, becomes the zero-configweb_searchdefault (auto-detect order 76, ahead of the other key-free fallbacks). A fresh install returns LLM-optimized, ranked dense excerpts with no key. The existing paid Parallel Search (parallel) is used whenPARALLEL_API_KEYis set.What is intentionally out of scope?
No core architectural change and no new SDK surface. The generic
web_searchtool stays provider-agnostic (no provider name in its description, no tool-call relabeling). Other providers untouched. Auto-detect still prefers a configured keyed provider over the keyless default.CHANGELOG.mdis left to release tooling.What does success look like?
Fresh install (no key) selects
parallel-free;PARALLEL_API_KEYselectsparallel; any other configured provider/key still wins; the paid REST path is unchanged.What should reviewers focus on?
Provider selection and default ordering (
src/web-search/runtime.ts,src/secrets/runtime-web-tools.shared.ts); the hand-rolled Streamable-HTTP MCP client; the sharedparallel-search-normalize.tsused by both runtimes; the doctor legacy-owner registry update; and that nothing provider-specific leaked into core.Linked context
Which issue does this close?
Closes #
Which issues, PRs, or discussions are related?
Related #85158 (added the paid Parallel
web_searchprovider that this builds on)Was this requested by a maintainer or owner?
Authored by an OpenClaw maintainer as intended work, not drive-by churn.
Real behavior proof (required for external PRs)
web_searchresolves toparallel-freeand returns live results from the free Search MCP; configured keys still win.~/.openclaw/openclaw.json; live calls tohttps://search.parallel.ai/mcp.# in another window pnpm openclaw tuiEvidence after fix: live screenshots from the run above. Search returns results with no key and no setup, and onboarding shows Parallel Search (Free) selected by default when no other API keys are present.
Search is working (no keys, no setup):
Onboarding shows Parallel Search (Free) — selected by default if no other API keys are present:
Observed result after fix: matches expected across all selection cases (no-key →
parallel-free;PARALLEL_API_KEY→parallel;BRAVE_API_KEY→brave; explicitprovider→ that provider).What was not tested: cross-OS beyond macOS; sustained-load quota behavior on the free MCP.
Proof limitations or environment constraints: free MCP results vary over time.
Tests and validation
Which commands did you run?
pnpm build(no[INEFFECTIVE_DYNAMIC_IMPORT]);pnpm tsgo:core+tsgo:extensions;oxfmt/oxlint;pnpm testonextensions/parallel,src/web-search,src/secrets/runtime-web-tools,src/config/config.web-search-provider,src/flows/search-setup,src/plugins/contracts/providers.contract, and the doctorlegacy-web-search-migratesuite.codex review --base origin/main: no actionable findings.What regression coverage was added or updated?
New
parallel-freeprovider tests + a dedicated MCP-client test (handshake, SSE/JSON parsing, envelope selection,model_name/session_idforwarding); contract/registration cases updated for the new provider id.What failed before this fix, if known?
N/A — additive feature.
If no test was added, why not?
Tests were added.
Risk checklist
Did user-visible behavior change? Yes — the no-credential
web_searchdefault changes from DuckDuckGo toparallel-free.Did config, environment, or migration behavior change? No new config/env;
parallel-freehas no legacy shape and is registered as non-migrated in the doctor owner map.Did security, auth, secrets, network, or tool execution behavior change? New outbound host
search.parallel.ai, routed through the existingwithTrustedWebSearchEndpointSSRF guard (same policy as the REST path); anonymous, no credentials sent.What is the highest-risk area?
Provider auto-detect selection — ensuring the keyless default never overrides a configured keyed provider.
How is that risk mitigated?
Selection is covered by unit tests across the no-key, key-present, and explicit-provider cases; the keyless default only applies when no credential is configured.
Current review state
What is the next action?
Mark ready for review.
What is still waiting on author, maintainer, CI, or external proof?
Nothing outstanding — local checks + codex clean; repro and screenshots included above.
Which bot or reviewer comments were addressed?
Pre-submission
codex reviewfindings were all resolved before opening (final pass: no actionable findings).