location_search
Convert city or region queries into the exact location names required by DataForSEO, enabling accurate local SEO data retrieval.
Instructions
Search for DataForSEO location names. Use this BEFORE other tools to resolve a city/region into the exact location_name format that DataForSEO requires (e.g. 'Orchard Park,New York,United States'). Free — costs 0 credits.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Location query (e.g. "Orchard Park" or "Austin TX") | |
| limit | No | Max results. Default: 10 |
Implementation Reference
- src/tools/locations.ts:20-26 (handler)The handler function that executes the location_search tool logic: calls API GET /v1/locations/search with query params and formats the result.
withErrorHandling(async ({ q, limit }) => { const params = new URLSearchParams({ q }); if (limit) params.set("limit", String(limit)); const result = await callApiGet(`/v1/locations/search?${params}`, getAuth()); return { content: [{ type: "text" as const, text: formatResult(result.data, result) }] }; }) ); - src/tools/locations.ts:15-18 (schema)Zod schema for location_search tool inputs: 'q' (string, required) and 'limit' (optional integer, 1-50, default 10).
{ q: z.string().min(1).describe('Location query (e.g. "Orchard Park" or "Austin TX")'), limit: z.number().int().min(1).max(50).optional().describe("Max results. Default: 10"), }, - src/tools/locations.ts:11-27 (registration)The registerLocationTools function registers the 'location_search' tool on the MCP server with its name, description, schema, and handler.
export function registerLocationTools(server: McpServer, getAuth: () => string) { server.tool( "location_search", "Search for DataForSEO location names. Use this BEFORE other tools to resolve a city/region into the exact location_name format that DataForSEO requires (e.g. 'Orchard Park,New York,United States'). Free — costs 0 credits.", { q: z.string().min(1).describe('Location query (e.g. "Orchard Park" or "Austin TX")'), limit: z.number().int().min(1).max(50).optional().describe("Max results. Default: 10"), }, READ_ONLY, withErrorHandling(async ({ q, limit }) => { const params = new URLSearchParams({ q }); if (limit) params.set("limit", String(limit)); const result = await callApiGet(`/v1/locations/search?${params}`, getAuth()); return { content: [{ type: "text" as const, text: formatResult(result.data, result) }] }; }) ); } - src/api-client.ts:132-138 (helper)formatResult helper used by the handler to format API response data with metadata (credits used/remaining).
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)}`; } - src/api-client.ts:94-130 (helper)callApiGet helper used by the handler to make GET requests to the underlying API.
export async function callApiGet( path: string, authHeader: string ): Promise<{ data: unknown; credits_used: number; credits_remaining: number; cached: boolean }> { const url = `${env.API_BASE_URL}${path}`; console.log(`[api] GET ${url} (auth: ${authHeader ? `${authHeader.slice(0, 15)}...` : "MISSING"})`); const response = await fetch(url, { method: "GET", headers: { Authorization: authHeader, }, signal: AbortSignal.timeout(60_000), }); if (!response.ok) { const text = await response.text(); console.error(`[api] ${response.status} ${response.statusText} from ${path}: ${text.slice(0, 200)}`); throw new Error(`API returned ${response.status}: ${text.slice(0, 200)}`); } const result = (await response.json()) as ApiResponse; if (result.status === "error") { throw new Error(result.error.message); } console.log(`[api] ${path} OK (${result.credits_used} credits used, ${result.credits_remaining} remaining)`); return { data: result.data, credits_used: result.credits_used, credits_remaining: result.credits_remaining, cached: result.cached, }; }