feat: add search_google_images MCP tool with SerpAPI integration#214
feat: add search_google_images MCP tool with SerpAPI integration#214sidneyswift merged 6 commits intotestfrom
Conversation
- Create lib/serpapi/ with config, types, and searchGoogleImages function - Register search_google_images tool in MCP server - Add comprehensive tests (9 test cases covering success, errors, defaults) The tool was previously defined in the chat project but never registered in the API's MCP server, so it was unavailable to the AI model.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces single web-search registration with a consolidated search tools registrar and adds SerpAPI-based Google Images support: config, types, URL builder, API client, and MCP tool registration with input validation and timeout handling. Changes
Sequence DiagramsequenceDiagram
participant Client as MCP Client
participant MCP as MCP Server
participant Handler as Google Images Handler
participant Config as SerpAPI Config
participant Builder as URL Builder
participant SerpAPI as SerpAPI
Client->>MCP: invoke `search_google_images` with options
MCP->>Handler: execute handler
Handler->>Config: getSerpApiKey()
Config-->>Handler: return apiKey
Handler->>Builder: buildSearchUrl(options, apiKey)
Builder-->>Handler: return full SerpAPI URL
Handler->>SerpAPI: HTTP GET (with 10s timeout)
SerpAPI-->>Handler: JSON response
Handler->>Handler: parse, limit, map results
Handler-->>MCP: getToolResultSuccess(images)
MCP-->>Client: deliver tool result
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ❌ 1❌ Failed checks (1 warning)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@lib/serpapi/types.ts`:
- Around line 17-24: Update the SerpApiResponse interface so images_results is
optional: change the images_results property to images_results?:
SerpApiImageResult[] to reflect runtime behavior, ensuring code that checks for
data.images_results (e.g., in searchGoogleImages.ts and usages like
response.images_results || []) aligns with the type; keep search_metadata
unchanged.
🧹 Nitpick comments (4)
lib/serpapi/searchGoogleImages.ts (3)
42-46: UnnecessaryContent-Typeheader on a GET request.GET requests have no body, so
Content-Type: application/jsonserves no purpose and may confuse readers into thinking a body is expected.Proposed fix
const response = await fetch(url, { method: "GET", - headers: { "Content-Type": "application/json" }, signal: controller.signal, });
25-35: API key appears in the URL — be mindful of logging.The
api_keyis embedded in the query string viaURLSearchParams. This is SerpAPI's expected auth mechanism, but ensure that request URLs are not logged by middleware, proxies, or error reporters, as the key would be exposed.
13-73: Function exceeds the 50-line guideline.At ~61 lines, this is slightly over the project's 50-line limit for domain functions. You could extract the parameter-building logic (Lines 19-35) into a small helper like
buildSearchParams(options)to bring this under the threshold and improve testability.As per coding guidelines, "Keep functions under 50 lines".
lib/mcp/tools/registerSearchGoogleImagesTool.ts (1)
14-19: Inconsistent defaultlimitbetween tool and search function.The tool defaults
limitto8(Line 69) and documents it as such here, butsearchGoogleImagesinternally defaults to10. While not a bug (the tool always passes its value), having two different defaults for the same parameter is a maintenance trap. Consider aligning them or removing the default fromsearchGoogleImagesto make the caller always explicit.
- Remove console.error from tool handler (principle 2: no production logging) - Remove comments that explain 'what' not 'why' (principle 14) - Keep one comment explaining SerpAPI's tbs parameter convention
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@lib/mcp/tools/registerSearchGoogleImagesTool.ts`:
- Around line 1-107: The file registering the tool (exported function
registerSearchGoogleImagesTool and schema searchGoogleImagesSchema) should be
relocated into a domain subfolder (e.g., a "search" domain) to match the
project's MCP tools structure; move the file into the new domain folder, update
any imports within it if needed, and then add an export/import entry in that
domain's index.ts so registerSearchGoogleImagesTool is exported and wired the
same way as other tools (mirror patterns used by artistSocials, images, youtube,
etc.). Ensure the exported function name and inputSchema remain unchanged and
adjust any module paths that imported this tool elsewhere to point to the new
domain index export.
- Around line 48-106: The tool registration registerSearchGoogleImagesTool is
missing domain placement and authentication: move the file into the images
domain folder and add account resolution before calling the API by extracting
authInfo from the handler's extra parameter and calling await
resolveAccountId(authInfo) (or similar project helper) and pass the resolved
accountId or auth context into searchGoogleImages; update the handler to
import/await resolveAccountId, use the resolved account info when invoking
searchGoogleImages, and keep the rest of the logic (mapping images,
getToolResultSuccess/getToolResultError) unchanged.
In `@lib/serpapi/searchGoogleImages.ts`:
- Line 34: The current construction of the request URL (const url =
`${SERPAPI_BASE_URL}/search.json?${params.toString()}`) embeds the api_key in
the query string which can leak credentials; update the request logic that
builds the URL and sends the HTTP call in searchGoogleImages.ts to remove
api_key from params and instead set it in the HTTP header "X-API-KEY" (or
equivalent) on the outgoing request (adjust the code that creates params and the
fetch/axios request options accordingly), ensuring SERPAPI_BASE_URL +
params.toString() no longer contains the secret; if header auth is not possible,
add a short comment documenting the decision and ensure any logging/path
instrumentation redacts the api_key query parameter.
🧹 Nitpick comments (4)
lib/serpapi/searchGoogleImages.ts (3)
40-44: UnnecessaryContent-Typeheader on a GET request.This is a GET request with no request body—
Content-Type: application/jsonhas no effect here and can be safely removed. Keeping it adds minor noise and could confuse future readers into thinking the request sends a JSON body.♻️ Suggested fix
const response = await fetch(url, { method: "GET", - headers: { "Content-Type": "application/json" }, signal: controller.signal, });
53-57: Mutating the response object before returning.
data.images_resultsis mutated in-place viaslice. This works but is a minor impurity. A cleaner approach would be to return a new object with the sliced array, keeping the function more aligned with pure-function principles.♻️ Suggested fix
const data: SerpApiResponse = await response.json(); - if (limit && data.images_results) { - data.images_results = data.images_results.slice(0, limit); - } - - return data; + return { + ...data, + images_results: data.images_results?.slice(0, limit) ?? data.images_results, + };As per coding guidelines,
lib/**/*.ts: "Pure functions when possible".
13-71: Function body slightly exceeds the 50-line guideline.The function is well-structured and reads clearly, but at ~58 lines it nudges past the 50-line limit. You could extract the URL/params construction (lines 18–34) into a small helper like
buildSerpApiUrl(options, apiKey)to trim this down and improve testability. Not urgent—just something to consider if you revisit this module.As per coding guidelines,
lib/**/*.ts: "Keep functions under 50 lines".lib/mcp/tools/registerSearchGoogleImagesTool.ts (1)
69-69: Defaultlimitmismatch between MCP tool and underlying function.The MCP tool defaults
limitto8(line 69), whilesearchGoogleImagesdefaults it to10(insearchGoogleImages.tsline 14). This won't cause a bug since the MCP tool explicitly passes its value, but the inconsistency could confuse maintainers. Consider aligning both defaults or extracting a shared constant.
lib/serpapi/searchGoogleImages.ts
Outdated
| if (tbs) params.append("tbs", tbs); | ||
| if (aspectRatio) params.append("imgar", aspectRatio); | ||
|
|
||
| const url = `${SERPAPI_BASE_URL}/search.json?${params.toString()}`; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n lib/serpapi/searchGoogleImages.tsRepository: recoupable/api
Length of output: 2741
🏁 Script executed:
# Search for logging middleware and request logging patterns
rg -n -i 'log.*url|log.*fetch|log.*request|console\.log.*url' --type=ts -g '!node_modules' -g '!*.test.*' -g '!*.spec.*' | head -50Repository: recoupable/api
Length of output: 3584
🏁 Script executed:
# Check for middleware setup files (Express, Next.js, etc.)
find . -type f -name '*middleware*' -o -name '*logger*' -o -name '*logging*' | grep -v node_modules | head -20Repository: recoupable/api
Length of output: 73
🏁 Script executed:
cat -n middleware.tsRepository: recoupable/api
Length of output: 3332
🏁 Script executed:
# Also search for any request/response logging in Next.js config or other setup files
rg -n -i 'fetch|request|response' middleware.ts --type=ts -A 5 -B 5Repository: recoupable/api
Length of output: 450
🏁 Script executed:
# Search for any logging libraries or error tracking that might log URLs
rg -n 'sentry|winston|pino|bunyan|morgan|logger' --type=ts -g '!node_modules' | head -30Repository: recoupable/api
Length of output: 40
Consider using request headers for API authentication instead of query parameters.
While SerpAPI documents api_key in the query string as their supported authentication method, embedding sensitive credentials in URLs can create unintended exposure if logging or error tracking is added later. The codebase currently has no middleware logging full URLs, so there's no immediate risk. However, for defense-in-depth, SerpAPI supports passing the API key via the X-API-KEY header. If this option is available, prefer it over query parameters to minimize accidental credential leakage in future logging, stack traces, or monitoring tools.
Alternatively, if staying with query parameters, document the decision and ensure any future logging middleware is configured to redact sensitive query parameters.
🤖 Prompt for AI Agents
In `@lib/serpapi/searchGoogleImages.ts` at line 34, The current construction of
the request URL (const url =
`${SERPAPI_BASE_URL}/search.json?${params.toString()}`) embeds the api_key in
the query string which can leak credentials; update the request logic that
builds the URL and sends the HTTP call in searchGoogleImages.ts to remove
api_key from params and instead set it in the HTTP header "X-API-KEY" (or
equivalent) on the outgoing request (adjust the code that creates params and the
fetch/axios request options accordingly), ensuring SERPAPI_BASE_URL +
params.toString() no longer contains the secret; if header auth is not possible,
add a short comment documenting the decision and ensure any logging/path
instrumentation redacts the api_key query parameter.
lib/mcp/tools/index.ts
Outdated
| registerContactTeamTool(server); | ||
| registerGetLocalTimeTool(server); | ||
| registerSearchWebTool(server); | ||
| registerSearchGoogleImagesTool(server); |
There was a problem hiding this comment.
Why not group the searchImage tool with existing searchWeb tool for a grouping of searchTools so our tools are better organized for Agentic usage?
- Group search tools in search/ domain subfolder (reviewer request) - Move registerSearchWebTool into search/ alongside Google Images - Create search/index.ts with registerAllSearchTools - Extract buildSearchUrl into separate helper (< 50 line functions) - Remove Content-Type header from GET request (no body) - Return new object instead of mutating SerpAPI response (pure function) - Align default limit via shared DEFAULT_IMAGE_LIMIT constant - Document SerpAPI api_key in URL as required by their API - Move test to search/__tests__/
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@lib/serpapi/buildSearchParams.ts`:
- Line 31: The code is appending the full aspect ratio names directly to SerpAPI
via params.append("imgar", aspectRatio) so unrecognized values are ignored;
update buildSearchParams to map the local aspect ratio values
("square","wide","tall","panoramic") to SerpAPI's expected abbreviations
("s","w","t","xw") before appending: add a small helper or inline mapping (e.g.,
mapAspectRatio or aspectRatioMap) that translates aspectRatio to its
abbreviation (or omits the param if no match) and then call
params.append("imgar", mappedValue) instead of using the raw aspectRatio.
🧹 Nitpick comments (2)
lib/mcp/tools/search/registerSearchGoogleImagesTool.ts (2)
63-93: MissingresolveAccountId()call — consistent withregisterSearchWebToolbut deviates from guideline.The coding guideline states MCP tools should "Use resolveAccountId() for authentication." While
registerSearchWebToolalso omits this, both tools allow unauthenticated callers to consume external API quota (SerpAPI / Perplexity). Consider adding authentication to both search tools, or document the intentional omission.If you do want to add it, the handler signature would need the
extraparameter:Sketch of auth addition
- async (args: SearchGoogleImagesArgs) => { + async (args: SearchGoogleImagesArgs, extra) => { + const authInfo = extra?.authInfo; + await resolveAccountId(authInfo); + const { query, limit = DEFAULT_IMAGE_LIMIT, imageSize, imageType, aspectRatio } = args;As per coding guidelines,
lib/mcp/tools/**/*.ts: "Use resolveAccountId() for authentication".
69-78: Consider extracting the result-mapping function for testability and reuse.The
.map()callback that transformsSerpApiImageResult→ tool result shape is a good candidate for a standalone pure function (e.g.,mapSerpApiImageToResult). This would make it independently testable and reusable if other tools need the same mapping.
SerpAPI expects abbreviated codes (s, w, t, xw) not full names (square, wide, tall, panoramic). Added inline mapping so the imgar parameter is correctly translated before appending.
Problem
Google Images tool (
search_google_images) was not working because it was never registered in the API's MCP server.The tool was defined in the chat project (
chat/lib/tools/searchGoogleImages.ts) and listed inchat/lib/tools/getMcpTools.ts, but that file was dead code — never imported anywhere. Since the actual tool serving happens via the API's MCP server, the tool simply didn't exist in the active tool set.Solution
Created the SerpAPI integration in the API project and registered the tool:
lib/serpapi/config.ts— SerpAPI URL constant and API key retrievallib/serpapi/types.ts— TypeScript interfaces for SerpAPI responseslib/serpapi/searchGoogleImages.ts— Fetch function for Google Images via SerpAPIlib/mcp/tools/registerSearchGoogleImagesTool.ts— MCP tool registrationlib/mcp/tools/index.ts— Added registration callTests
9 test cases covering:
Note
Ensure
SERPAPI_API_KEYis set in the API project's environment variables (should already exist in the chat project's env).Follow-up
The following dead code in the chat project can be cleaned up in a separate PR:
chat/lib/tools/getMcpTools.ts(never imported)chat/lib/tools/searchGoogleImages.ts(only imported by dead getMcpTools)chat/lib/serpapi/*(only imported by dead searchGoogleImages tool)Summary by CodeRabbit
New Features
Refactor