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.
| "id": "serpapi", | ||
| "name": "SerpApi Search", | ||
| "description": "Web search provider backed by SerpApi (Google Light, with 100+ engines). Specialized SerpApi tools follow in subsequent releases.", | ||
| "description": "Web search provider plus specialized SerpApi tools for news, flights, hotels, maps, shopping, YouTube, scholar, finance, events, and 100+ search engines.", |
There was a problem hiding this comment.
Fixed the entrypoint description now matches the manifest
| description: | ||
| "Web search provider backed by SerpApi (Google Light). Specialized SerpApi tools for news, flights, hotels, maps, shopping, YouTube, scholar, finance, and more follow in subsequent releases.", | ||
| "Web search provider plus specialized SerpApi tools for news, flights, hotels, maps, shopping, YouTube, scholar, finance, events, and 100+ search engines.", |
| 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.
| cat: { | ||
| type: "string", | ||
| description: "Search category ID (default: 0 = all categories).", | ||
| }, |
| 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, |
There was a problem hiding this comment.
The currency param now defaults to USD.
| const nextPageToken = readStringParam(args, "next_page_token"); | ||
| const num = readNumberParam(args, "num", { integer: true }) ?? undefined; | ||
| if (num !== undefined && !nextPageToken && !topicId && !query) { | ||
| throw new Error( | ||
| "serpapi_maps_reviews: num cannot be used on the initial page without next_page_token, topic_id, or query", | ||
| ); | ||
| } |
| const cfg = resolveToolConfig(api, ctx); | ||
| const searchAssist = readBooleanArg(args, "search_assist"); | ||
| const searchAssistParam = searchAssist === true ? "true" : undefined; | ||
| const m = readNumberParam(args, "m", { integer: true }); |
| mkt: mkt ?? undefined, | ||
| cc: cc ?? undefined, | ||
| location: readStringParam(args, "location") ?? undefined, | ||
| safeSearch: readStringParam(args, "safeSearch") ?? undefined, |
| allowedParams: ALLOWED_PARAMS, | ||
| params: { | ||
| url: readStringParam(args, "url", { required: true }), | ||
| type: readStringParam(args, "type") ?? undefined, |
| enum: [ | ||
| "relevanceblender", | ||
| "relevanceblender", | ||
| "price-asc-rank", | ||
| "price-desc-rank", | ||
| "review-rank", | ||
| "date-desc-rank", | ||
| "exact-aware-popularity-rank", | ||
| ], |
| engine: "yahoo", | ||
| results: organicResults, | ||
| related_searches: raw.related_searches ?? null, | ||
| serpapi_pagination: raw.serpapi_pagination ?? null, |
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.