feat: add 30 specialized SerpApi tools#1
Conversation
There was a problem hiding this comment.
Pull request overview
This PR expands the SerpApi OpenClaw plugin from just a web_search provider to a broader suite of specialized SerpApi-backed tools, and wires them into the plugin contract/registration and documentation.
Changes:
- Adds shared tool helpers (
resolveToolConfig,readBooleanArg) to support consistent runtime config resolution and boolean argument parsing. - Introduces 30 specialized SerpApi tools (plus a barrel export) and registers them in the plugin entrypoint.
- Updates plugin contracts and user-facing documentation (README + SerpApi skill docs) to reflect the new tool surface area.
Reviewed changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils.ts | Adds shared helpers for tool config resolution and boolean argument parsing. |
| src/tools/amazon.ts | Adds serpapi_amazon tool implementation. |
| src/tools/amazon-product.ts | Adds serpapi_amazon_product tool implementation. |
| src/tools/autocomplete.ts | Adds serpapi_autocomplete tool implementation. |
| src/tools/bing.ts | Adds serpapi_bing tool implementation. |
| src/tools/duckduckgo.ts | Adds serpapi_duckduckgo tool implementation. |
| src/tools/ebay.ts | Adds serpapi_ebay tool implementation. |
| src/tools/ebay-product.ts | Adds serpapi_ebay_product tool implementation. |
| src/tools/events.ts | Adds serpapi_events tool implementation. |
| src/tools/facebook-profile.ts | Adds serpapi_facebook_profile tool implementation. |
| src/tools/finance.ts | Adds serpapi_finance tool implementation. |
| src/tools/flights.ts | Adds serpapi_flights tool implementation. |
| src/tools/hotels.ts | Adds serpapi_hotels tool implementation. |
| src/tools/immersive-product.ts | Adds serpapi_immersive_product tool implementation. |
| src/tools/instagram-profile.ts | Adds serpapi_instagram_profile tool implementation. |
| src/tools/jobs.ts | Adds serpapi_jobs tool implementation. |
| src/tools/lens.ts | Adds serpapi_lens tool implementation. |
| src/tools/maps.ts | Adds serpapi_maps tool implementation. |
| src/tools/maps-reviews.ts | Adds serpapi_maps_reviews tool implementation. |
| src/tools/news.ts | Adds serpapi_news tool implementation. |
| src/tools/scholar.ts | Adds serpapi_scholar tool implementation. |
| src/tools/shopping.ts | Adds serpapi_shopping tool implementation. |
| src/tools/trends.ts | Adds serpapi_trends tool implementation. |
| src/tools/tripadvisor.ts | Adds serpapi_tripadvisor tool implementation. |
| src/tools/walmart.ts | Adds serpapi_walmart tool implementation. |
| src/tools/walmart-product.ts | Adds serpapi_walmart_product tool implementation. |
| src/tools/weather.ts | Adds serpapi_weather tool implementation. |
| src/tools/yahoo.ts | Adds serpapi_yahoo tool implementation. |
| src/tools/youtube.ts | Adds serpapi_youtube tool implementation. |
| src/tools/youtube-video.ts | Adds serpapi_youtube_video tool implementation. |
| src/tools/youtube-transcript.ts | Adds serpapi_youtube_transcript tool implementation. |
| src/tools/index.ts | Adds barrel exports for all specialized tools. |
| index.ts | Registers the 30 specialized tools in the plugin entrypoint. |
| openclaw.plugin.json | Declares the tool IDs under contracts.tools and updates plugin metadata. |
| skills/serpapi/SKILL.md | Expands skill documentation with per-tool parameters and usage guidance. |
| README.md | Updates README to reflect the additional specialized tools and docs link. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import type { AnyAgentTool } from "openclaw/plugin-sdk/plugin-entry"; | ||
| import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime"; | ||
| import { jsonResult, readStringParam } from "openclaw/plugin-sdk/provider-web-search"; | ||
| import { callSerpApi } from "../serpapi-client.js"; | ||
| import { type SerpApiToolCtx, resolveToolConfig } from "../utils.js"; | ||
|
|
||
| const ALLOWED_PARAMS = ["search_query", "hl", "sp", "zero_trace"] as const; | ||
|
|
||
| function extract(raw: Record<string, unknown>): Record<string, unknown> { | ||
| return { | ||
| engine: "youtube", | ||
| channel_results: raw.channel_results ?? [], | ||
| video_results: raw.video_results ?? [], | ||
| shorts_results: raw.shorts_results ?? [], | ||
| }; | ||
| } |
There was a problem hiding this comment.
Thanks for flagging this. We're intentionally not wrapping these fields with wrapWebContent for now.
The engine youtube returns the raw SerpApi result objects as structured JSON.
| execute: async (_toolCallId: string, args: Record<string, unknown>, signal?: AbortSignal) => { | ||
| const cfg = resolveToolConfig(api, ctx); | ||
| const raw = await callSerpApi({ | ||
| cfg, | ||
| engine: "google_flights", | ||
| allowedParams: ALLOWED_PARAMS, | ||
| params: { | ||
| departure_id: readStringParam(args, "departure_id", { required: true }), | ||
| arrival_id: readStringParam(args, "arrival_id", { required: true }), | ||
| outbound_date: readStringParam(args, "outbound_date", { required: true }), | ||
| return_date: readStringParam(args, "return_date") ?? undefined, | ||
| type: readStringParam(args, "type") ?? undefined, | ||
| adults: readNumberParam(args, "adults", { integer: true }) ?? undefined, | ||
| currency: readStringParam(args, "currency") ?? undefined, | ||
| gl: readStringParam(args, "gl") ?? undefined, | ||
| }, | ||
| signal, | ||
| }); | ||
| return jsonResult(extract(raw)); | ||
| }, |
| import { | ||
| jsonResult, | ||
| readNumberParam, | ||
| readStringParam, | ||
| } from "openclaw/plugin-sdk/provider-web-search"; |
There was a problem hiding this comment.
Thanks for flagging this. We're intentionally not wrapping these fields with wrapWebContent for now.
The engine google_news returns the raw SerpApi result objects as structured JSON.
| function extract(raw: Record<string, unknown>, maxCount: number): Record<string, unknown> { | ||
| const results = Array.isArray(raw.news_results) | ||
| ? (raw.news_results as unknown[]).slice(0, maxCount) | ||
| : []; | ||
| return { | ||
| engine: "google_news", | ||
| results, | ||
| menu_links: raw.menu_links ?? [], | ||
| related_topics: raw.related_topics ?? [], | ||
| related_publications: raw.related_publications ?? [], | ||
| }; | ||
| } |
There was a problem hiding this comment.
Thanks for flagging this. We're intentionally not wrapping these fields with wrapWebContent for now.
The engine google_news returns the raw SerpApi result objects as structured JSON.
| import { | ||
| jsonResult, | ||
| readNumberParam, | ||
| readStringParam, | ||
| } from "openclaw/plugin-sdk/provider-web-search"; |
There was a problem hiding this comment.
Thanks for flagging this. We're intentionally not wrapping these fields with wrapWebContent for now.
The engine google_scholar returns the raw SerpApi result objects as structured JSON.
| function extract(raw: Record<string, unknown>, maxCount: number): Record<string, unknown> { | ||
| const results = Array.isArray(raw.organic_results) | ||
| ? (raw.organic_results as unknown[]).slice(0, maxCount) | ||
| : []; | ||
| return { | ||
| engine: "google_scholar", | ||
| results, | ||
| related_searches: raw.related_searches ?? [], | ||
| }; | ||
| } |
There was a problem hiding this comment.
Thanks for flagging this. We're intentionally not wrapping these fields with wrapWebContent for now.
The engine google_scholar returns the raw SerpApi result objects as structured JSON.
| const cfg = resolveToolConfig(api, ctx); | ||
| const searchAssist = readBooleanArg(args, "search_assist"); | ||
| const searchAssistParam = searchAssist === true ? "true" : undefined; | ||
| const m = readNumberParam(args, "m", { integer: true }); |
| execute: async (_toolCallId: string, args: Record<string, unknown>, signal?: AbortSignal) => { | ||
| const cfg = resolveToolConfig(api, ctx); | ||
| const raw = await callSerpApi({ | ||
| cfg, | ||
| engine: "google_autocomplete", | ||
| allowedParams: ALLOWED_PARAMS, | ||
| params: { | ||
| q: readStringParam(args, "query", { required: true }), | ||
| gl: readStringParam(args, "gl") ?? undefined, | ||
| hl: readStringParam(args, "hl") ?? undefined, | ||
| cp: readNumberParam(args, "cp", { integer: true }) ?? undefined, | ||
| client: readStringParam(args, "client") ?? undefined, | ||
| }, |
There was a problem hiding this comment.
We should not send an explicit cp default here.
Unlike safeSearch: "Moderate" or type: "all", cp is not a fixed enum default it is derived from the query string. Computing query.length in the plugin assumes we replicate Google/SerpApi cursor semantics exactly, including edge cases we do not control (UTF-16 code units vs graphemes, combining characters, surrogate pairs, etc.). A wrong guess would change suggestion ranking for non-ASCII queries.
Summary
Adds 30 specialized SerpApi tools to the plugin.
Each tool follows the same pattern: a
createSerpApi<X>Tool(api, ctx)factory returning anAnyAgentTool, a per-engineALLOWED_PARAMSsecurity allowlist, and anextract()shaping function.Tools
serpapi_amazonserpapi_amazon_productserpapi_autocompleteserpapi_bingserpapi_duckduckgoserpapi_ebayserpapi_ebay_productserpapi_eventsserpapi_facebook_profileserpapi_financeserpapi_flightsserpapi_hotelsserpapi_immersive_productpage_tokenfrom Shopping results.serpapi_instagram_profileserpapi_jobsserpapi_lensserpapi_mapsserpapi_maps_reviewsserpapi_newsserpapi_scholarserpapi_shoppingserpapi_trendsserpapi_tripadvisorserpapi_walmartserpapi_walmart_productserpapi_weatherserpapi_yahooserpapi_youtubeserpapi_youtube_transcriptserpapi_youtube_videoChanges
src/tools/*.ts— 30 tool implementations + barrel (src/tools/index.ts).src/utils.ts— sharedresolveToolConfig/readBooleanArgtool helpers.index.ts— register all 30 tools alongside the web search provider.openclaw.plugin.json— declare all 30 ids incontracts.tools.skills/serpapi/SKILL.md— per-tool parameter and usage docs.README.md— updated tool list.