organic_serp
Retrieve full Google SERP results for a keyword and location, including organic listings, local pack, ads, and more. Costs 1 credit.
Instructions
Get full Google SERP results including organic listings, local pack, ads, People Also Ask, AI overview, LSA ads, and knowledge panel. Costs 1 credit.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| keyword | Yes | Search keyword (e.g. "best plumber near me") | |
| location | Yes | City and state (e.g. "Orchard Park, NY") |
Implementation Reference
- src/tools/serp.ts:40-43 (handler)The handler function that executes the organic_serp tool logic — calls the API endpoint /v1/serp/organic with keyword and location, then formats the result.
withErrorHandling(async ({ keyword, location }) => { const result = await callApi("/v1/serp/organic", { keyword, location }, getAuth()); return { content: [{ type: "text" as const, text: formatResult(result.data, result) }] }; }) - src/tools/serp.ts:35-39 (schema)Input schema for the organic_serp tool — requires keyword (string) and location (string), both as Zod schemas.
{ keyword: z.string().describe('Search keyword (e.g. "best plumber near me")'), location: z.string().describe('City and state (e.g. "Orchard Park, NY")'), }, READ_ONLY, - src/tools/serp.ts:32-44 (registration)Registration of the organic_serp tool with the MCP server via server.tool(), including name, description, schema, read-only hint, and handler.
server.tool( "organic_serp", "Get full Google SERP results including organic listings, local pack, ads, People Also Ask, AI overview, LSA ads, and knowledge panel. Costs 1 credit.", { keyword: z.string().describe('Search keyword (e.g. "best plumber near me")'), location: z.string().describe('City and state (e.g. "Orchard Park, NY")'), }, READ_ONLY, withErrorHandling(async ({ keyword, location }) => { const result = await callApi("/v1/serp/organic", { keyword, location }, getAuth()); return { content: [{ type: "text" as const, text: formatResult(result.data, result) }] }; }) ); - src/api-client.ts:132-158 (helper)formatResult helper used by the handler to format the API response data with credit usage metadata.
export function formatResult( data: unknown, meta: { credits_used: number; credits_remaining: number; cached: boolean } ): string { const metaLine = `[${meta.credits_used} credit${meta.credits_used !== 1 ? "s" : ""} used | ${meta.credits_remaining} remaining${meta.cached ? " | cached" : ""}]`; return `${metaLine}\n\n${JSON.stringify(data, null, 2)}`; } type ToolResult = { content: { type: "text"; text: string }[]; isError?: boolean }; /** Wrap an MCP tool handler so thrown errors always surface as MCP error content */ export function withErrorHandling<T>( fn: (args: T) => Promise<ToolResult> ): (args: T) => Promise<ToolResult> { return async (args) => { try { return await fn(args); } catch (err) { const message = err instanceof Error ? err.message : String(err); console.error(`[mcp] Tool error: ${message}`); return { content: [{ type: "text" as const, text: `Error: ${message}` }], isError: true, }; } }; } - src/api-client.ts:143-158 (helper)withErrorHandling wrapper that catches errors thrown by the handler and returns them as MCP error content.
export function withErrorHandling<T>( fn: (args: T) => Promise<ToolResult> ): (args: T) => Promise<ToolResult> { return async (args) => { try { return await fn(args); } catch (err) { const message = err instanceof Error ? err.message : String(err); console.error(`[mcp] Tool error: ${message}`); return { content: [{ type: "text" as const, text: `Error: ${message}` }], isError: true, }; } }; }