Skip to content

feat: add Tavily as a bundled web search plugin with search and extract tools#49200

Merged
gumadeiras merged 15 commits into
openclaw:mainfrom
lakshyaag-tavily:feat/add-tavily-plugin
Mar 20, 2026
Merged

feat: add Tavily as a bundled web search plugin with search and extract tools#49200
gumadeiras merged 15 commits into
openclaw:mainfrom
lakshyaag-tavily:feat/add-tavily-plugin

Conversation

@lakshyaag-tavily

@lakshyaag-tavily lakshyaag-tavily commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: OpenClaw's web_search tool supports Brave, Firecrawl, Gemini, Grok, Kimi, and Perplexity but not Tavily Search API.
  • Why it matters: Tavily provides structured, LLM-optimized search results with configurable search depth, topic filtering, AI-generated answer summaries, domain filters, and content extraction from URLs (including JS-rendered pages).
  • What changed: Added Tavily as a bundled plugin (extensions/tavily/): web search provider registration (autoDetectOrder: 70), two dedicated tools (tavily_search, tavily_extract), a bundled skill, API key resolution (TAVILY_API_KEY / config), Zod schema + types, onboarding wizard entry, SecretRef surface registration, contract tests, and docs (dedicated page + web.md updates).
  • What did NOT change (scope boundary): No changes to existing providers or their behavior. No changes to web_fetch. No changes to the UI dashboard or gateway server logic.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • New web_search provider option: "tavily".
  • New config section: tools.web.search.tavily with apiKey and baseUrl fields.
  • New env var support: TAVILY_API_KEY.
  • Auto-detection: if TAVILY_API_KEY is set and no higher-priority provider key is found, Tavily is auto-selected (lowest priority, order 70).
  • New dedicated tool tavily_search with Tavily-specific parameters: search_depth (basic/advanced), topic (general/news/finance), include_answer, time_range, include_domains, exclude_domains, and up to 20 results.
  • New dedicated tool tavily_extract for URL content extraction with query-focused chunking, extract_depth (basic/advanced), and image inclusion.
  • Bundled skill teaches the agent when to use web_search vs tavily_search vs tavily_extract.
  • Onboarding wizard now includes Tavily in the search provider picker. Selecting Tavily auto-enables the plugin.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? Yes — new TAVILY_API_KEY env var and tools.web.search.tavily.apiKey config field, following the same SecretInput / SecretRef pattern as all other providers.
  • New/changed network calls? Yes — new outbound POST to https://api.tavily.com/search and https://api.tavily.com/extract (or user-configured baseUrl), using the existing withTrustedWebToolsEndpoint dispatcher (proxy-aware, SSRF-guarded, timeout-enforced).
  • Command/tool execution surface changed? Yesweb_search tool gains Tavily as a provider option. Two new tools registered: tavily_search, tavily_extract.
  • Data access scope changed? No
  • Risk + mitigation: The Tavily API key is handled identically to existing provider keys (SecretRef, env fallback, sensitive registration). Network calls go through the existing trusted-endpoint dispatcher. All external content is wrapped with untrusted: true markers via wrapWebContent / wrapExternalContent.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22+ / Bun
  • Model/provider: N/A (tool-level change)
  • Integration/channel (if any): N/A
  • Relevant config (redacted): { tools: { web: { search: { provider: "tavily", tavily: { apiKey: "tvly-..." } } } } }

Steps

  1. Set TAVILY_API_KEY=tvly-... in environment (or configure via openclaw configure --section web).
  2. Run tavily_search with query: "test query".
  3. Verify structured results with title, url, snippet fields are returned.

Expected

  • Tavily returns structured search results; optional answer field when include_answer: true.

Actual

  • Confirmed via unit tests (see Evidence).

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Tests added/updated:

  • extensions/tavily/index.test.ts — plugin registration, web search provider + tool ownership
  • src/plugins/web-search-providers.test.ts — ordering: Tavily at position 70
  • src/plugins/contracts/registry.contract.test.ts — contract: Tavily owns ["tavily"] web search provider and ["tavily_search", "tavily_extract"] tools
  • src/commands/onboard-search.test.ts — 7 providers in onboarding picker, Tavily last
  • src/config/config.web-search-provider.test.ts — auto-detection selects Tavily when only TAVILY_API_KEY is present

All gates pass:

  • pnpm build
  • pnpm check
  • pnpm test:extension tavily ✅ (2 tests)
  • pnpm test:contracts ✅ (114 tests)

Human Verification (required)

  • Verified scenarios: All new tests pass. Plugin registration correctly wires web search provider + 2 tools. Auto-detection selects Tavily when only TAVILY_API_KEY is present and falls back to Brave when no keys exist. Onboarding picker lists all 7 providers with Tavily last. Contract test validates ownership. Build produces no warnings.
  • Edge cases checked: Missing API key returns setup hint. Blank/whitespace baseUrl falls back to default. Provider ordering respects autoDetectOrder: 70 (last position). Plugin auto-enables when selected in onboarding
  • What you did not verify: Live Tavily API calls (mocked in tests). UI dashboard display of Tavily config (no UI changes in this PR).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes — purely additive; no existing behavior changed.
  • Config/env changes? Yes — new optional tools.web.search.tavily config section and TAVILY_API_KEY env var.
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Set plugins.entries.tavily.enabled: false, or set tools.web.search.provider to any other provider, or remove TAVILY_API_KEY from environment.
  • Files/config to restore: N/A (additive change).
  • Known bad symptoms reviewers should watch for: missing_tavily_api_key errors if provider is set to "tavily" without a key configured.

Risks and Mitigations

  • Risk: Tavily API rate limits or availability issues could affect users who auto-detect this provider.
    • Mitigation: Tavily is lowest priority in auto-detection (position 70, last); users only get it if no other provider key is configured. Explicit provider: "tavily" is opt-in.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation commands Command implementations size: L labels Mar 17, 2026
@lakshyaag-tavily
lakshyaag-tavily marked this pull request as ready for review March 17, 2026 19:08
@lakshyaag-tavily
lakshyaag-tavily requested a review from a team as a code owner March 17, 2026 19:08
@greptile-apps

greptile-apps Bot commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds Tavily as a bundled web search plugin, following the existing plugin architecture closely. It registers a web_search provider, two dedicated tools (tavily_search, tavily_extract), a bundled skill, config schema updates, onboarding wizard integration, SecretRef surface registration, and contract tests. The overall structure is consistent with how Firecrawl and other providers are integrated.

Key issues found:

  • Extract cache key is missing baseUrl (tavily-client.ts line ~209): runTavilySearch correctly includes baseUrl in its cache key but runTavilyExtract does not. If a user reconfigures baseUrl, stale responses from the previous endpoint will be served from the in-process EXTRACT_CACHE until process restart.
  • resolveEndpoint silently drops the per-endpoint pathname for custom base URLs with a path (tavily-client.ts lines 54–68): When baseUrl contains a non-root path (e.g. https://proxy.example.com/tavily/), both runTavilySearch and runTavilyExtract will POST to the exact same URL because the pathname argument is ignored. If this is intentional "full-URL override" mode, it should be documented.
  • External image URLs in tavily_extract results are not wrapped (tavily-client.ts line 250): r.images is passed through raw while the sibling rawContent and content fields are both wrapped with wrapExternalContent. This is an inconsistency in the untrusted-content handling pattern.
  • DEFAULT_TAVILY_BASE_URL is defined in two places (config.ts line 5 and tavily-client.ts line 29): the client module should import and reuse the constant already exported from config.ts.

Confidence Score: 3/5

  • Safe to merge after fixing the extract cache key bug; remaining issues are low-risk but worth addressing before shipping.
  • The PR is well-structured and follows existing patterns faithfully across all integration points (schema, onboarding, secret handling, network dispatch). One confirmed caching bug (extract cache key missing baseUrl) could surface incorrect results for users who change their baseUrl without restarting. The resolveEndpoint behavior with custom-path base URLs is ambiguous and could confuse proxy users. These issues are self-contained to the new extension and do not affect any existing providers or behavior.
  • extensions/tavily/src/tavily-client.ts — extract cache key bug and resolveEndpoint behavior need attention before this ships.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/tavily/src/tavily-client.ts
Line: 54-69

Comment:
**Custom `baseUrl` with path silently ignores `pathname` for both endpoints**

When `baseUrl` is set to something like `https://proxy.example.com/tavily/`, the function detects a non-root pathname and returns the URL verbatim — discarding the `pathname` argument (`/search` or `/extract`). Both `runTavilySearch` and `runTavilyExtract` then POST to exactly the same URL. A user who configures `baseUrl: "https://proxy.example.com/api/v2/"` expecting the SDK to append `/search` or `/extract` will have both tools silently route to an unintended endpoint.

If the intent is "treat a URL-with-path as a complete endpoint override", that should be documented. If the intent is to support a base URL with a path prefix (e.g. a reverse proxy), consider always appending the pathname:

```typescript
url.pathname = url.pathname.replace(/\/$/, "") + pathname;
return url.toString();
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/tavily/src/tavily-client.ts
Line: 250

Comment:
**External image URLs not wrapped as untrusted content**

`r.images` is passed through from the Tavily API response without any `wrapExternalContent` treatment. The sibling fields `rawContent` and `content` on the same result object are both wrapped (lines 243–249). Image URLs from an external API are still external content and should be treated consistently — at minimum for auditing purposes.

```suggestion
    ...(Array.isArray(r.images)
      ? { images: (r.images as string[]).map((img) => wrapExternalContent(String(img), { source: "web_fetch", includeWarning: false })) }
      : {}),
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/tavily/src/tavily-client.ts
Line: 29

Comment:
**`DEFAULT_TAVILY_BASE_URL` duplicated across modules**

This constant is already exported from `./config.js` (line 5 of `config.ts`). Defining it again here creates two sources of truth. If the default URL ever changes, one of them will be missed.

```suggestion
import {
  DEFAULT_CACHE_TTL_MINUTES,
  normalizeCacheKey,
  readCache,
  readResponseText,
  resolveCacheTtlMs,
  writeCache,
} from "openclaw/plugin-sdk/provider-web-search";
import { wrapExternalContent, wrapWebContent } from "openclaw/plugin-sdk/security-runtime";
import {
  DEFAULT_TAVILY_BASE_URL,
  resolveTavilyApiKey,
  resolveTavilyBaseUrl,
  resolveTavilyExtractTimeoutSeconds,
  resolveTavilySearchTimeoutSeconds,
} from "./config.js";
```

Then remove the local `const DEFAULT_TAVILY_BASE_URL = "https://api.tavily.com";` declaration on this line.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/tavily/src/tavily-client.ts
Line: 209-218

Comment:
**Extract cache key missing `baseUrl`**

The extract cache key omits `baseUrl`, but the search cache key (lines 122–135) correctly includes it. If the user changes their `baseUrl` configuration, stale results from the previous endpoint will be served from cache until the in-memory `EXTRACT_CACHE` is naturally evicted. Since `EXTRACT_CACHE` is a module-level `Map`, this persists for the life of the process.

`baseUrl` should be computed before the cache key block (mirroring how `runTavilySearch` resolves it on line 120 before building the cache key) and included in the JSON object passed to `normalizeCacheKey`.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 376a6b5

Comment thread extensions/tavily/src/tavily-client.ts
Comment thread extensions/tavily/src/tavily-client.ts Outdated
Comment thread extensions/tavily/src/tavily-client.ts Outdated
Comment thread extensions/tavily/src/tavily-client.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 376a6b5e41

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread extensions/tavily/src/tavily-client.ts
Comment thread src/config/config.web-search-provider.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5cd38e3548

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/config/zod-schema.agent-runtime.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d6cb45f03

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/plugins/web-search-providers.ts Outdated
Comment thread extensions/tavily/src/tavily-search-provider.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31f5d691bf

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/config/zod-schema.agent-runtime.ts Outdated
Comment thread src/secrets/target-registry-data.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90f3d2bffa

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread extensions/tavily/src/tavily-search-provider.ts
Comment thread extensions/tavily/src/tavily-extract-tool.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b989db918d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread extensions/tavily/src/tavily-extract-tool.ts Outdated
Comment thread extensions/tavily/src/tavily-client.ts
@gumadeiras gumadeiras self-assigned this Mar 20, 2026
@gumadeiras
gumadeiras force-pushed the feat/add-tavily-plugin branch 3 times, most recently from c07d927 to 1875c7a Compare March 20, 2026 03:10

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1875c7aa02

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/commands/onboard-search.ts
Comment thread extensions/tavily/src/tavily-search-provider.ts
@gumadeiras
gumadeiras force-pushed the feat/add-tavily-plugin branch from 1875c7a to bf7c4ac Compare March 20, 2026 04:03

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf7c4ac87f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/tavily/openclaw.plugin.json
@openclaw-barnacle openclaw-barnacle Bot added the agents Agent runtime and tooling label Mar 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25501d3a0c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/tavily/openclaw.plugin.json
yinuotxie pushed a commit to yinuotxie/openclaw that referenced this pull request Mar 20, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
fuller-stack-dev pushed a commit to fuller-stack-dev/openclaw that referenced this pull request Mar 20, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
fuller-stack-dev pushed a commit to fuller-stack-dev/openclaw that referenced this pull request Mar 20, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
frankekn pushed a commit to artwalker/openclaw that referenced this pull request Mar 23, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
Nachx639 pushed a commit to Nachx639/clawdbot that referenced this pull request Jun 17, 2026
…ct tools (openclaw#49200)

Merged via squash.

Prepared head SHA: ece9226
Co-authored-by: lakshyaag-tavily <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling commands Command implementations docs Improvements or additions to documentation size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants