business_profile
Retrieve a complete Google Business Profile including name, rating, reviews, address, phone, website, hours, categories, attributes, photos, description, and verification status.
Instructions
Get a complete Google Business Profile including name, rating, reviews, address, phone, website, hours, categories, attributes, photos count, description, and verification status. Costs 2 credits.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| business_name | Yes | Business name | |
| location | Yes | City and state | |
| place_id | No | Google Place ID for exact match |
Implementation Reference
- src/server.ts:34-36 (registration)Registers business_profile tool via registerBusinessTools call in the MCP server initialization.
registerAccountTools(server, getAuth); registerSerpTools(server, getAuth); registerBusinessTools(server, getAuth); - src/tools/business.ts:11-29 (handler)The registerBusinessTools function registers the 'business_profile' tool with its handler, which calls the API endpoint /v1/business/profile with business_name, location, and optional place_id parameters.
export function registerBusinessTools(server: McpServer, getAuth: () => string) { server.tool( "business_profile", "Get a complete Google Business Profile including name, rating, reviews, address, phone, website, hours, categories, attributes, photos count, description, and verification status. Costs 2 credits.", { business_name: z.string().describe("Business name"), location: z.string().describe("City and state"), place_id: z.string().optional().describe("Google Place ID for exact match"), }, READ_ONLY, withErrorHandling(async ({ business_name, location, place_id }) => { const result = await callApi( "/v1/business/profile", { business_name, location, ...(place_id && { place_id }) }, getAuth() ); return { content: [{ type: "text" as const, text: formatResult(result.data, result) }] }; }) ); - src/tools/business.ts:15-19 (schema)Zod schema defining input parameters: business_name (string, required), location (string, required), place_id (string, optional).
{ business_name: z.string().describe("Business name"), location: z.string().describe("City and state"), place_id: z.string().optional().describe("Google Place ID for exact match"), }, - src/api-client.ts:25-92 (helper)The callApi helper function that makes authenticated POST requests to the API. Used by the business_profile handler.
export async function callApi( path: string, body: Record<string, unknown>, authHeader: string, timeoutMs = 60_000 ): Promise<{ data: unknown; credits_used: number; credits_remaining: number; cached: boolean }> { const url = `${env.API_BASE_URL}${path}`; console.log(`[api] POST ${url} (timeout: ${timeoutMs / 1000}s, auth: ${authHeader ? `${authHeader.slice(0, 15)}...` : "MISSING"})`); const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Authorization: authHeader, }, body: JSON.stringify(body), signal: AbortSignal.timeout(timeoutMs), }); if (!response.ok) { const text = await response.text(); console.error(`[api] ${response.status} ${response.statusText} from ${path}: ${text.slice(0, 200)}`); // Try to parse as structured error try { const result = JSON.parse(text) as ApiErrorResponse; if (result.status === "error") { const err = result.error; const reqId = result.request_id ? ` [request_id: ${result.request_id}]` : ""; throw new Error( err.required_credits ? `${err.message} (requires ${err.required_credits} credits, balance: ${err.current_balance})${reqId}` : `${err.message}${reqId}` ); } } catch (parseErr) { if (parseErr instanceof Error && parseErr.message !== "error") { // Re-throw if it's our structured error from above if (!text.includes('"status":"error"')) { throw new Error(`API returned ${response.status}: ${text.slice(0, 200)}`); } throw parseErr; } } throw new Error(`API returned ${response.status}: ${text.slice(0, 200)}`); } const result = (await response.json()) as ApiResponse; if (result.status === "error") { const err = (result as ApiErrorResponse).error; const reqId = (result as ApiErrorResponse).request_id ? ` [request_id: ${(result as ApiErrorResponse).request_id}]` : ""; throw new Error( err.required_credits ? `${err.message} (requires ${err.required_credits} credits, balance: ${err.current_balance})${reqId}` : `${err.message}${reqId}` ); } 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, }; } - src/api-client.ts:143-158 (helper)The withErrorHandling wrapper that catches errors from the tool 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, }; } }; }