qa
Retrieve questions and answers from a Google Business Profile, including authors, dates, and upvotes. Each request costs 1 credit.
Instructions
Get Questions & Answers from a Google Business Profile. Returns questions with their answers, authors, dates, and upvotes. Costs 1 credit.
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:36-36 (registration)The 'qa' tool is registered via registerBusinessTools(server, getAuth) call, which is imported from src/tools/business.ts
registerBusinessTools(server, getAuth); - src/tools/business.ts:50-67 (handler)The 'qa' tool handler function. Defines the tool with name 'qa', description, input schema (business_name, location, place_id), and the async handler that calls the API at /v1/business/qa.
server.tool( "qa", "Get Questions & Answers from a Google Business Profile. Returns questions with their answers, authors, dates, and upvotes. Costs 1 credit.", { 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/qa", { business_name, location, ...(place_id && { place_id }) }, getAuth() ); return { content: [{ type: "text" as const, text: formatResult(result.data, result) }] }; }) ); - src/tools/business.ts:53-57 (schema)Input schema for the 'qa' tool using Zod: business_name (string), location (string), and optional place_id (string).
{ 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 used by the 'qa' tool handler to make POST requests to the API endpoint.
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 provides error handling for the 'qa' tool handler.
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, }; } }; }