@getmcpads/google-ads-mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/platforms/google-ads/tools.ts","../src/platforms/google-ads/types.ts","../src/platforms/google-ads/metric-catalog.ts","../src/platforms/google-ads/dimension-catalog.ts","../src/platforms/google-ads/compatibility-rules.ts","../src/platforms/google-ads/filter-catalog.ts","../src/platforms/google-ads/query-planner.ts","../src/platforms/google-ads/calculated-metrics.ts","../src/core/logger.ts","../src/core/errors.ts","../src/core/rate-limiter.ts","../src/platforms/google-ads/read-rpc.ts","../src/platforms/google-ads/client.ts","../src/platforms/google-ads/keyword-planner.ts","../src/platforms/google-ads/discovery-tools.ts","../src/platforms/google-ads/resources.ts","../src/platforms/google-ads/writes.ts","../src/platforms/google-ads/index.ts","../src/config.ts"],"sourcesContent":["/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { GoogleAdsConfig } from \"./config.js\";\nimport { registerGoogleAds } from \"./platforms/google-ads/index.js\";\nimport { logger } from \"./core/logger.js\";\n\nexport const PACKAGE_VERSION = \"1.0.0\";\n\nexport function createServer(config: GoogleAdsConfig): McpServer {\n const server = new McpServer(\n { name: \"google-ads-mcp\", version: PACKAGE_VERSION },\n { capabilities: { tools: { listChanged: true }, resources: { subscribe: false, listChanged: true } } },\n );\n registerGoogleAds(server, config);\n logger.system(\n `google-ads-mcp v${PACKAGE_VERSION} ready, writes ${config.enableWrites ? \"enabled\" : \"disabled\"}`,\n );\n return server;\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { GoogleAdsClient } from \"./client.js\";\nimport { planQuery, generateQueryPreview, resolveMetricApiField } from \"./query-planner.js\";\nimport { validateQuerySelection } from \"./compatibility-rules.js\";\nimport { formatMcpToolError } from \"../../core/errors.js\";\nimport type { GoogleAdsConfig } from \"../../config.js\";\nimport { GOOGLE_ADS_API_VERSION, stripCustomerId, type GoogleAdsRow } from \"./types.js\";\nimport { registerGoogleAdsKeywordPlannerTools } from \"./keyword-planner.js\";\nimport { registerGoogleAdsDiscoveryTools } from \"./discovery-tools.js\";\nimport { registerGoogleAdsReadOnlyRpcTool } from \"./read-rpc.js\";\n\nconst customerIdSchema = z.string().describe(\"Google Ads customer ID (without dashes, e.g., 1234567890)\");\nconst loginCustomerIdSchema = z.string().optional().describe(\"MCC Manager account ID (required for sub-accounts managed by an MCC)\");\nconst isoDateSchema = z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Expected date format YYYY-MM-DD\");\nconst numericIdSchema = z.string().regex(/^\\d+$/, \"Expected a numeric Google Ads ID\");\nconst gaqlEnumSchema = z.string().regex(/^[A-Z0-9_]+$/, \"Expected an uppercase Google Ads enum value\");\nconst ZERO_METRIC_LIMIT_WARNING = \"All returned rows have zero metric values and rowCount equals the requested limit. The response may be truncated before active entities; retry with a higher limit or pass orderBy on a non-zero metric.\";\n\ntype AgentResponseRecord = Record<string, unknown>;\n\nfunction isAgentResponseRecord(value: unknown): value is AgentResponseRecord {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction hasOwnField(value: AgentResponseRecord, field: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, field);\n}\n\nfunction getDebugRecord(payload: AgentResponseRecord): AgentResponseRecord {\n return isAgentResponseRecord(payload[\"debug\"]) ? payload[\"debug\"] : {};\n}\n\nfunction getRequestCount(payload: AgentResponseRecord): number {\n const debug = getDebugRecord(payload);\n const requestCount = debug[\"requestCount\"];\n return typeof requestCount === \"number\" ? requestCount : 1;\n}\n\nfunction getWarnings(payload: AgentResponseRecord): unknown[] {\n if (Array.isArray(payload[\"warnings\"])) return payload[\"warnings\"];\n\n const debug = getDebugRecord(payload);\n return Array.isArray(debug[\"warnings\"]) ? debug[\"warnings\"] : [];\n}\n\nfunction withAgentResponseContract(data: unknown): unknown {\n if (!isAgentResponseRecord(data)) return data;\n\n return {\n ...data,\n warnings: hasOwnField(data, \"warnings\") ? data[\"warnings\"] : getWarnings(data),\n limitations: hasOwnField(data, \"limitations\") ? data[\"limitations\"] : [],\n nextActions: hasOwnField(data, \"nextActions\") ? data[\"nextActions\"] : [],\n debug: {\n ...getDebugRecord(data),\n source: \"google_ads\",\n apiVersion: GOOGLE_ADS_API_VERSION,\n requestCount: getRequestCount(data),\n },\n };\n}\n\nfunction ok(data: unknown) {\n return { content: [{ type: \"text\" as const, text: JSON.stringify(withAgentResponseContract(data), null, 2) }] };\n}\n\nfunction numericValue(value: unknown): number | null {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n }\n return null;\n}\n\nfunction metricFieldKeys(metrics: string[]): string[] {\n const keys = new Set<string>();\n for (const metric of metrics) {\n const apiField = resolveMetricApiField(metric);\n if (apiField) keys.add(apiField);\n if (apiField === \"metrics.cost_micros\") keys.add(\"metrics.cost\");\n }\n return [...keys];\n}\n\nfunction allReturnedMetricValuesAreZero(rows: Array<Record<string, unknown>>, metrics: string[]): boolean {\n const keys = metricFieldKeys(metrics);\n if (rows.length === 0 || keys.length === 0) return false;\n\n let sawMetricValue = false;\n for (const row of rows) {\n for (const key of keys) {\n const value = numericValue(row[key]);\n if (value === null) continue;\n sawMetricValue = true;\n if (value !== 0) return false;\n }\n }\n\n return sawMetricValue;\n}\n\nfunction oneLineGaql(query: string): string {\n return query.replace(/\\s+/g, \" \").trim();\n}\n\nfunction quoteGaqlString(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\");\n}\n\nfunction buildWhere(clauses: string[]): string {\n return clauses.length > 0 ? ` WHERE ${clauses.join(\" AND \")}` : \"\";\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction parseIsoDate(value: string): Date {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(value);\n if (!match) throw new Error(`Invalid date \"${value}\". Expected YYYY-MM-DD.`);\n\n const year = Number(match[1]);\n const month = Number(match[2]);\n const day = Number(match[3]);\n const parsed = new Date(Date.UTC(year, month - 1, day));\n\n if (\n parsed.getUTCFullYear() !== year ||\n parsed.getUTCMonth() !== month - 1 ||\n parsed.getUTCDate() !== day\n ) {\n throw new Error(`Invalid calendar date \"${value}\".`);\n }\n\n return parsed;\n}\n\nfunction formatIsoDate(date: Date): string {\n return date.toISOString().slice(0, 10);\n}\n\nfunction addDays(date: Date, days: number): Date {\n const next = new Date(date);\n next.setUTCDate(next.getUTCDate() + days);\n return next;\n}\n\nfunction daysBetween(start: Date, end: Date): number {\n return Math.floor((end.getTime() - start.getTime()) / 86_400_000);\n}\n\nfunction requireDateRange(startDate?: string, endDate?: string): string[] {\n if (!startDate || !endDate) {\n throw new Error(\"startDate and endDate are required together in YYYY-MM-DD format.\");\n }\n const start = parseIsoDate(startDate);\n const end = parseIsoDate(endDate);\n if (start > end) throw new Error(\"startDate must be on or before endDate.\");\n return [`segments.date BETWEEN '${startDate}' AND '${endDate}'`];\n}\n\nfunction optionalDateRange(startDate?: string, endDate?: string): string[] {\n if (!startDate && !endDate) return [];\n return requireDateRange(startDate, endDate);\n}\n\nfunction normalizeGetInsightsDateInput(input: {\n startDate?: string;\n endDate?: string;\n datePreset?: string;\n}): { startDate?: string; endDate?: string; datePreset?: string; warnings: string[] } {\n if (input.datePreset !== \"LAST_90_DAYS\") {\n return { ...input, warnings: [] };\n }\n\n const end = addDays(parseIsoDate(formatIsoDate(new Date())), -1);\n const start = addDays(end, -89);\n const startDate = formatIsoDate(start);\n const endDate = formatIsoDate(end);\n\n return {\n startDate,\n endDate,\n datePreset: undefined,\n warnings: [`Translated preset LAST_90_DAYS to BETWEEN ${startDate} AND ${endDate} because LAST_90_DAYS is not a native GAQL DURING value.`],\n };\n}\n\nfunction normalizeChangeEventRange(\n startDate: string | undefined,\n endDate: string | undefined\n): { startDate: string; endExclusiveDate: string; warnings: string[] } {\n const warnings: string[] = [];\n const today = parseIsoDate(formatIsoDate(new Date()));\n const oldestAllowed = addDays(today, -30);\n\n let end = endDate ? parseIsoDate(endDate) : today;\n if (end > today) {\n warnings.push(\"endDate was in the future; using today because change_event only supports recent history.\");\n end = today;\n }\n if (end < oldestAllowed) {\n warnings.push(\"endDate was older than the change_event 30-day retention window; using today.\");\n end = today;\n }\n\n let start = startDate ? parseIsoDate(startDate) : addDays(end, -14);\n if (start < oldestAllowed) {\n warnings.push(\"startDate was older than the change_event 30-day retention window; clamped to the oldest supported date.\");\n start = oldestAllowed;\n }\n\n if (start > end) throw new Error(\"startDate must be on or before endDate after applying the 30-day change_event window.\");\n\n if (daysBetween(start, end) > 30) {\n warnings.push(\"change_event supports a maximum 30-day window; startDate was clamped.\");\n start = addDays(end, -30);\n if (start < oldestAllowed) start = oldestAllowed;\n }\n\n return {\n startDate: formatIsoDate(start),\n endExclusiveDate: formatIsoDate(addDays(end, 1)),\n warnings,\n };\n}\n\nasync function runGaqlWithFallback(\n client: GoogleAdsClient,\n customerId: string,\n attempts: Array<{ label: string; gaql: string; failureWarning?: string }>\n): Promise<{ rows: GoogleAdsRow[]; gaql: string; queryLabel: string; warnings: string[] }> {\n const warnings: string[] = [];\n let lastError: unknown;\n\n for (const attempt of attempts) {\n try {\n const rows = await client.searchStream(customerId, attempt.gaql);\n if (warnings.length > 0) warnings.push(`Executed fallback query \"${attempt.label}\".`);\n return { rows, gaql: attempt.gaql, queryLabel: attempt.label, warnings };\n } catch (error) {\n lastError = error;\n if (attempt.failureWarning) {\n warnings.push(`${attempt.failureWarning}: ${getErrorMessage(error)}`);\n }\n }\n }\n\n throw lastError;\n}\n\nexport function registerGoogleAdsTools(server: McpServer, config: GoogleAdsConfig): void {\n const client = new GoogleAdsClient({\n developerToken: config.developerToken,\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n refreshToken: config.refreshToken,\n loginCustomerId: config.loginCustomerId,\n });\n\n // ── 1. google_ads_list_accounts ────────────────────────────────────\n server.tool(\n \"google_ads_list_accounts\",\n \"List Google Ads customer accounts accessible with the current credentials. If an accessible customer is a manager account, also attempts to include enabled child accounts from customer_client.\",\n {},\n async () => {\n try {\n const customers = await client.getAllCustomers();\n const warnings: string[] = [];\n const accountMap = new Map<string, typeof customers[number]>();\n\n for (const customer of customers) {\n accountMap.set(stripCustomerId(customer.id), customer);\n }\n\n const managerCustomers = customers.filter((customer) => customer.manager);\n for (const manager of managerCustomers) {\n try {\n const childAccounts = await client.getClientAccounts(manager.id);\n for (const child of childAccounts) {\n const childId = stripCustomerId(child.id);\n if (!accountMap.has(childId)) {\n accountMap.set(childId, {\n ...child,\n resourceName: child.resourceName || `customers/${childId}`,\n });\n }\n }\n } catch (error) {\n warnings.push(`Could not discover child accounts for manager ${stripCustomerId(manager.id)}. Use google_ads_get_account_hierarchy for details. ${getErrorMessage(error)}`);\n }\n }\n\n const accounts = Array.from(accountMap.values());\n return ok({\n accounts,\n count: accounts.length,\n directlyAccessibleCount: customers.length,\n discoveredChildCount: Math.max(0, accounts.length - customers.length),\n managerAccountCount: managerCustomers.length,\n warnings,\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 2. google_ads_get_account_details ──────────────────────────────\n server.tool(\n \"google_ads_get_account_details\",\n \"Get detailed information for a specific Google Ads customer account.\",\n { customerId: customerIdSchema },\n async ({ customerId }) => {\n try {\n const customer = await client.getCustomer(customerId);\n return ok(customer);\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 3. google_ads_run_gaql ─────────────────────────────────────────\n server.tool(\n \"google_ads_run_gaql\",\n `Execute a raw GAQL (Google Ads Query Language) query. Full flexibility for any reporting need.\nExample: SELECT campaign.name, metrics.impressions FROM campaign WHERE campaign.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS ORDER BY metrics.impressions DESC LIMIT 100`,\n {\n customerId: customerIdSchema,\n query: z.string().min(10).describe(\"GAQL query string (SELECT ... FROM ... WHERE ...)\"),\n },\n async ({ customerId, query }) => {\n try {\n const rows = await client.searchStream(customerId, query);\n return ok({ data: rows, rowCount: rows.length, gaql: query });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 4. google_ads_get_insights ─────────────────────────────────────\n server.tool(\n \"google_ads_get_insights\",\n `Query Google Ads performance insights with intelligent query planning. Auto-generates GAQL, handles metric/segment incompatibilities by splitting queries.\nUse google-ads://metrics for available metrics, google-ads://dimensions for dimensions.`,\n {\n customerId: customerIdSchema,\n resource: z.string()\n .optional().default(\"campaign\").describe(\"GAQL FROM clause resource type (campaign, ad_group, ad_group_ad, keyword_view, shopping_performance_view, asset_group, geographic_view, video, search_term_view, landing_page_view, etc.)\"),\n metrics: z.array(z.string()).min(1).describe(\"Metric keys (e.g., impressions, clicks, cost_micros, conversions)\"),\n dimensions: z.array(z.string()).optional().describe(\"Dimension keys (e.g., date, campaignName, device)\"),\n startDate: z.string().optional().describe(\"Start date YYYY-MM-DD\"),\n endDate: z.string().optional().describe(\"End date YYYY-MM-DD\"),\n datePreset: z.enum([\n \"TODAY\", \"YESTERDAY\", \"LAST_7_DAYS\", \"LAST_14_DAYS\", \"LAST_30_DAYS\",\n \"LAST_90_DAYS\", \"THIS_MONTH\", \"LAST_MONTH\", \"THIS_QUARTER\", \"LAST_QUARTER\",\n ]).optional().describe(\"Predefined date range\"),\n orderBy: z.string().optional().describe(\"Optional GAQL field to order by, e.g. metrics.impressions or campaign.name\"),\n orderDirection: z.enum([\"ASC\", \"DESC\"]).optional().default(\"DESC\"),\n limit: z.number().int().min(1).max(10000).optional().default(500),\n },\n async ({ customerId, resource, metrics, dimensions, startDate, endDate, datePreset, orderBy, orderDirection, limit }) => {\n try {\n const startTime = Date.now();\n const dateInput = normalizeGetInsightsDateInput({ startDate, endDate, datePreset });\n const plan = planQuery({\n customerId,\n resource: resource as import(\"./types.js\").GoogleAdsResourceType,\n metrics,\n dimensions: dimensions ?? [],\n filters: [],\n startDate: dateInput.startDate,\n endDate: dateInput.endDate,\n datePreset: dateInput.datePreset as import(\"./types.js\").GoogleAdsDatePreset | undefined,\n orderBy,\n orderDirection,\n limit,\n });\n\n if (plan.errors.length > 0) {\n return ok({\n error: \"Query validation failed\",\n errors: plan.errors,\n warnings: plan.warnings,\n gaqlPreview: generateQueryPreview(plan),\n });\n }\n\n const result = await client.executeQuery({\n customerId,\n resource: resource as import(\"./types.js\").GoogleAdsResourceType,\n metrics,\n dimensions: dimensions ?? [],\n filters: [],\n startDate: dateInput.startDate,\n endDate: dateInput.endDate,\n datePreset: dateInput.datePreset as import(\"./types.js\").GoogleAdsDatePreset | undefined,\n orderBy,\n orderDirection,\n limit,\n });\n\n const debugWarnings = Array.isArray(result.debug.warnings) ? result.debug.warnings : [];\n const debugErrors = Array.isArray(result.debug.errors) ? result.debug.errors : [];\n const zeroMetricLimitWarnings = result.data.length === limit && allReturnedMetricValuesAreZero(result.data, metrics)\n ? [ZERO_METRIC_LIMIT_WARNING]\n : [];\n const warnings = [...dateInput.warnings, ...debugWarnings, ...zeroMetricLimitWarnings];\n\n return ok({\n status: debugErrors.length > 0 ? \"error\" : \"ok\",\n data: result.data,\n rowCount: result.data.length,\n errors: debugErrors,\n warnings,\n debug: {\n ...result.debug,\n warnings,\n executionTimeMs: Date.now() - startTime,\n gaqlPreview: generateQueryPreview(plan),\n },\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 5. google_ads_get_campaigns ────────────────────────────────────\n server.tool(\n \"google_ads_get_campaigns\",\n \"List campaigns for a Google Ads account with status, budget, channel type, and bidding strategy.\",\n {\n customerId: customerIdSchema,\n statusFilter: z.enum([\"ENABLED\", \"PAUSED\", \"REMOVED\"]).optional(),\n limit: z.number().int().min(1).max(1000).optional().default(100),\n },\n async ({ customerId, statusFilter, limit }) => {\n try {\n let gaql = `SELECT campaign.id, campaign.name, campaign.status, campaign.advertising_channel_type, campaign.bidding_strategy_type, campaign_budget.amount_micros FROM campaign`;\n if (statusFilter) gaql += ` WHERE campaign.status = '${statusFilter}'`;\n gaql += ` ORDER BY campaign.name ASC LIMIT ${limit}`;\n const rows = await client.searchStream(customerId, gaql);\n return ok({ campaigns: rows, count: rows.length });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 6. google_ads_get_adgroups ─────────────────────────────────────\n server.tool(\n \"google_ads_get_adgroups\",\n \"List ad groups for a Google Ads account, optionally filtered by campaign.\",\n {\n customerId: customerIdSchema,\n campaignId: z.string().optional().describe(\"Filter by campaign ID\"),\n statusFilter: z.enum([\"ENABLED\", \"PAUSED\", \"REMOVED\"]).optional(),\n limit: z.number().int().min(1).max(1000).optional().default(100),\n },\n async ({ customerId, campaignId, statusFilter, limit }) => {\n try {\n let gaql = `SELECT ad_group.id, ad_group.name, ad_group.status, ad_group.type, campaign.id, campaign.name FROM ad_group`;\n const where: string[] = [];\n if (campaignId) where.push(`campaign.id = ${campaignId}`);\n if (statusFilter) where.push(`ad_group.status = '${statusFilter}'`);\n if (where.length > 0) gaql += ` WHERE ${where.join(\" AND \")}`;\n gaql += ` ORDER BY ad_group.name ASC LIMIT ${limit}`;\n const rows = await client.searchStream(customerId, gaql);\n return ok({ adGroups: rows, count: rows.length });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 7. google_ads_get_keyword_performance ───────────────────────────\n server.tool(\n \"google_ads_get_keyword_performance\",\n \"Get keyword-level performance data from keyword_view resource. Shows quality score, impressions, clicks, cost.\",\n {\n customerId: customerIdSchema,\n startDate: z.string().describe(\"Start date YYYY-MM-DD\"),\n endDate: z.string().describe(\"End date YYYY-MM-DD\"),\n limit: z.number().int().min(1).max(1000).optional().default(100),\n },\n async ({ customerId, startDate, endDate, limit }) => {\n try {\n const gaql = `SELECT ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, ad_group_criterion.quality_info.quality_score, campaign.name, ad_group.name, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions, metrics.ctr FROM keyword_view WHERE segments.date BETWEEN '${startDate}' AND '${endDate}' AND ad_group_criterion.status = 'ENABLED' ORDER BY metrics.impressions DESC LIMIT ${limit}`;\n const rows = await client.searchStream(customerId, gaql);\n return ok({ keywords: rows, count: rows.length });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 8. google_ads_validate_query ───────────────────────────────────\n server.tool(\n \"google_ads_validate_query\",\n \"Validate metric/dimension/resource compatibility BEFORE executing a query. Checks segment restrictions and resource availability.\",\n {\n metrics: z.array(z.string()).min(1).describe(\"Metric keys to validate\"),\n dimensions: z.array(z.string()).optional().describe(\"Dimension keys to validate\"),\n resource: z.string()\n .optional().default(\"campaign\"),\n },\n async ({ metrics, dimensions, resource }) => {\n try {\n const result = validateQuerySelection(metrics, dimensions ?? [], resource as import(\"./types.js\").GoogleAdsResourceType, dimensions ?? []);\n return ok(result);\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 9. google_ads_health_check ────────────────────────────────────\n server.tool(\n \"google_ads_health_check\",\n \"Read-only connectivity check. Verifies credential presence, Google Ads API access, login customer visibility, API version, and actionable warnings without returning secrets.\",\n {},\n async () => {\n const warnings: string[] = [];\n const credentialPresence = {\n developerToken: Boolean(config.developerToken),\n clientId: Boolean(config.clientId),\n clientSecret: Boolean(config.clientSecret),\n refreshToken: Boolean(config.refreshToken),\n loginCustomerId: Boolean(config.loginCustomerId),\n };\n\n try {\n const accessibleCustomers = await client.getAllCustomers();\n const loginCustomerId = config.loginCustomerId ? stripCustomerId(config.loginCustomerId) : null;\n const customerIds = new Set(accessibleCustomers.map(customer => stripCustomerId(customer.id)));\n\n if (accessibleCustomers.length === 0) {\n warnings.push(\"No accessible customers were returned. Verify the OAuth user has Google Ads access and the developer token is approved.\");\n }\n if (loginCustomerId && !customerIds.has(loginCustomerId)) {\n warnings.push(\"GOOGLE_ADS_LOGIN_CUSTOMER_ID is configured but was not returned by listAccessibleCustomers; verify MCC access if child account queries fail.\");\n }\n if (!loginCustomerId && accessibleCustomers.some(customer => customer.manager)) {\n warnings.push(\"Manager accounts are accessible but GOOGLE_ADS_LOGIN_CUSTOMER_ID is not set. Set it to the MCC ID when querying managed child accounts.\");\n }\n\n return ok({\n status: \"ok\",\n apiVersion: GOOGLE_ADS_API_VERSION,\n credentialsPresent: credentialPresence,\n loginCustomerId,\n accessibleCustomers,\n accessibleCustomerCount: accessibleCustomers.length,\n warnings,\n });\n } catch (error) {\n warnings.push(\"Could not list accessible customers. Check OAuth refresh token, developer token status, and Google Ads account permissions.\");\n return ok({\n status: \"error\",\n apiVersion: GOOGLE_ADS_API_VERSION,\n credentialsPresent: credentialPresence,\n loginCustomerId: config.loginCustomerId ? stripCustomerId(config.loginCustomerId) : null,\n accessibleCustomers: [],\n accessibleCustomerCount: 0,\n error: getErrorMessage(error),\n warnings,\n });\n }\n },\n );\n\n // ── 10. google_ads_get_account_hierarchy ───────────────────────────\n server.tool(\n \"google_ads_get_account_hierarchy\",\n \"List accessible customers and, where possible, manager/client relationships from GAQL customer_client. Falls back to accessible customers if hierarchy queries are unavailable.\",\n {\n managerCustomerId: loginCustomerIdSchema,\n includeInactive: z.boolean().optional().default(false).describe(\"Include non-ENABLED customer_client links\"),\n },\n async ({ managerCustomerId, includeInactive }) => {\n try {\n const warnings: string[] = [];\n const accessibleCustomers = await client.getAllCustomers();\n const managerIds = managerCustomerId\n ? [stripCustomerId(managerCustomerId)]\n : (config.loginCustomerId\n ? [stripCustomerId(config.loginCustomerId)]\n : accessibleCustomers.filter(customer => customer.manager).map(customer => stripCustomerId(customer.id)));\n\n const relations: Array<Record<string, unknown>> = [];\n\n if (managerIds.length === 0) {\n warnings.push(\"No manager customer was detected. Returning accessible customers only.\");\n }\n\n for (const managerId of managerIds) {\n const where = [\"customer_client.level <= 10\"];\n if (!includeInactive) where.push(\"customer_client.status = 'ENABLED'\");\n\n const gaql = oneLineGaql(`\n SELECT\n customer_client.resource_name,\n customer_client.client_customer,\n customer_client.id,\n customer_client.descriptive_name,\n customer_client.currency_code,\n customer_client.time_zone,\n customer_client.manager,\n customer_client.test_account,\n customer_client.hidden,\n customer_client.level,\n customer_client.status\n FROM customer_client\n ${buildWhere(where)}\n ORDER BY customer_client.level ASC, customer_client.descriptive_name ASC\n `);\n\n try {\n const rows = await client.searchStream(managerId, gaql);\n for (const row of rows) {\n const customerClient = row.customerClient as Record<string, unknown> | undefined;\n if (!customerClient) continue;\n const customerId = String(customerClient.id ?? \"\");\n relations.push({\n managerCustomerId: managerId,\n clientCustomerId: customerId,\n clientCustomer: customerClient.clientCustomer,\n descriptiveName: customerClient.descriptiveName,\n currencyCode: customerClient.currencyCode,\n timeZone: customerClient.timeZone,\n manager: customerClient.manager,\n testAccount: customerClient.testAccount,\n hidden: customerClient.hidden,\n level: customerClient.level,\n status: customerClient.status,\n resourceName: customerClient.resourceName,\n isSelfLink: customerId === managerId || customerClient.level === \"0\" || customerClient.level === 0,\n });\n }\n } catch (error) {\n warnings.push(`Could not query customer_client for manager ${managerId}; returning accessible customers fallback for that branch. ${getErrorMessage(error)}`);\n }\n }\n\n return ok({\n accessibleCustomers,\n accessibleCustomerCount: accessibleCustomers.length,\n inspectedManagerCustomerIds: managerIds,\n relations,\n relationCount: relations.length,\n warnings,\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 11. google_ads_get_conversion_actions ─────────────────────────\n server.tool(\n \"google_ads_get_conversion_actions\",\n \"List conversion actions with status, type, category, primary/include-in-conversions flags, owner customer, and last activity dates when supported.\",\n {\n customerId: customerIdSchema,\n statusFilter: z.enum([\"ENABLED\", \"HIDDEN\", \"REMOVED\"]).optional(),\n limit: z.number().int().min(1).max(1000).optional().default(1000),\n },\n async ({ customerId, statusFilter, limit }) => {\n try {\n const where = statusFilter ? buildWhere([`conversion_action.status = '${statusFilter}'`]) : \"\";\n const richGaql = oneLineGaql(`\n SELECT\n conversion_action.resource_name,\n conversion_action.id,\n conversion_action.name,\n conversion_action.status,\n conversion_action.type,\n conversion_action.category,\n conversion_action.primary_for_goal,\n conversion_action.include_in_conversions_metric,\n conversion_action.owner_customer,\n metrics.conversion_last_conversion_date,\n metrics.conversion_last_received_request_date_time\n FROM conversion_action\n ${where}\n ORDER BY conversion_action.name ASC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n conversion_action.resource_name,\n conversion_action.id,\n conversion_action.name,\n conversion_action.status,\n conversion_action.type,\n conversion_action.category,\n conversion_action.primary_for_goal,\n conversion_action.include_in_conversions_metric,\n conversion_action.owner_customer\n FROM conversion_action\n ${where}\n ORDER BY conversion_action.name ASC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"conversion_action_with_last_activity\", gaql: richGaql, failureWarning: \"Conversion action query with last activity metrics failed\" },\n { label: \"conversion_action_minimal\", gaql: fallbackGaql },\n ]);\n\n return ok({ conversionActions: result.rows, count: result.rows.length, gaql: result.gaql, warnings: result.warnings });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 12. google_ads_get_change_events ───────────────────────────────\n server.tool(\n \"google_ads_get_change_events\",\n \"Fetch recent change_event rows. Enforces Google Ads constraints: date window within the last 30 days and LIMIT <= 10000.\",\n {\n customerId: customerIdSchema,\n startDate: isoDateSchema.optional().describe(\"Start date YYYY-MM-DD. Defaults to 14 days before endDate.\"),\n endDate: isoDateSchema.optional().describe(\"End date YYYY-MM-DD. Defaults to today.\"),\n resourceType: gaqlEnumSchema.optional().describe(\"Optional ChangeEventResourceType filter, e.g. CAMPAIGN or AD_GROUP_AD\"),\n operation: gaqlEnumSchema.optional().describe(\"Optional ResourceChangeOperation filter, e.g. CREATE, UPDATE, REMOVE\"),\n userEmail: z.string().email().optional().describe(\"Optional user email filter\"),\n limit: z.number().int().min(1).max(10000).optional().default(1000),\n },\n async ({ customerId, startDate, endDate, resourceType, operation, userEmail, limit }) => {\n try {\n const range = normalizeChangeEventRange(startDate, endDate);\n const where = [\n `change_event.change_date_time >= '${range.startDate}'`,\n `change_event.change_date_time < '${range.endExclusiveDate}'`,\n ];\n if (resourceType) where.push(`change_event.change_resource_type = '${resourceType}'`);\n if (operation) where.push(`change_event.resource_change_operation = '${operation}'`);\n if (userEmail) where.push(`change_event.user_email = '${quoteGaqlString(userEmail)}'`);\n\n const gaql = oneLineGaql(`\n SELECT\n change_event.resource_name,\n change_event.change_date_time,\n change_event.user_email,\n change_event.client_type,\n change_event.change_resource_type,\n change_event.change_resource_name,\n change_event.resource_change_operation,\n change_event.changed_fields,\n change_event.campaign,\n change_event.ad_group\n FROM change_event\n ${buildWhere(where)}\n ORDER BY change_event.change_date_time DESC\n LIMIT ${limit}\n `);\n\n const rows = await client.searchStream(customerId, gaql);\n return ok({\n changeEvents: rows,\n count: rows.length,\n effectiveDateRange: { startDate: range.startDate, endExclusiveDate: range.endExclusiveDate },\n gaql,\n warnings: range.warnings,\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 13. google_ads_get_recommendations ─────────────────────────────\n server.tool(\n \"google_ads_get_recommendations\",\n \"List Google Ads recommendations with type, resource, campaign/ad group links, dismissed state, and impact when supported.\",\n {\n customerId: customerIdSchema,\n typeFilter: gaqlEnumSchema.optional().describe(\"Optional RecommendationType enum filter\"),\n includeDismissed: z.boolean().optional().default(false),\n limit: z.number().int().min(1).max(1000).optional().default(1000),\n },\n async ({ customerId, typeFilter, includeDismissed, limit }) => {\n try {\n const where: string[] = [];\n if (!includeDismissed) where.push(\"recommendation.dismissed = FALSE\");\n if (typeFilter) where.push(`recommendation.type = '${typeFilter}'`);\n\n const richGaql = oneLineGaql(`\n SELECT\n recommendation.resource_name,\n recommendation.type,\n recommendation.campaign,\n recommendation.campaigns,\n recommendation.ad_group,\n recommendation.campaign_budget,\n recommendation.dismissed,\n recommendation.impact,\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name\n FROM recommendation\n ${buildWhere(where)}\n ORDER BY recommendation.type ASC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n recommendation.resource_name,\n recommendation.type,\n recommendation.campaign,\n recommendation.campaigns,\n recommendation.ad_group,\n recommendation.campaign_budget,\n recommendation.dismissed\n FROM recommendation\n ${buildWhere(where)}\n ORDER BY recommendation.type ASC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"recommendations_with_impact\", gaql: richGaql, failureWarning: \"Recommendation query with attributed resources/impact failed\" },\n { label: \"recommendations_minimal\", gaql: fallbackGaql },\n ]);\n\n return ok({ recommendations: result.rows, count: result.rows.length, gaql: result.gaql, warnings: result.warnings });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 14. google_ads_get_budgets ────────────────────────────────────\n server.tool(\n \"google_ads_get_budgets\",\n \"List campaign budgets with amount, status, delivery method, and recommended budget fields when supported.\",\n {\n customerId: customerIdSchema,\n statusFilter: z.enum([\"ENABLED\", \"REMOVED\", \"UNKNOWN\", \"UNSPECIFIED\"]).optional(),\n limit: z.number().int().min(1).max(1000).optional().default(1000),\n },\n async ({ customerId, statusFilter, limit }) => {\n try {\n const where = statusFilter ? buildWhere([`campaign_budget.status = '${statusFilter}'`]) : \"\";\n const richGaql = oneLineGaql(`\n SELECT\n campaign_budget.resource_name,\n campaign_budget.id,\n campaign_budget.name,\n campaign_budget.status,\n campaign_budget.delivery_method,\n campaign_budget.period,\n campaign_budget.type,\n campaign_budget.amount_micros,\n campaign_budget.total_amount_micros,\n campaign_budget.explicitly_shared,\n campaign_budget.reference_count,\n campaign_budget.has_recommended_budget,\n campaign_budget.recommended_budget_amount_micros,\n campaign_budget.recommended_budget_estimated_change_weekly_clicks,\n campaign_budget.recommended_budget_estimated_change_weekly_cost_micros,\n campaign_budget.recommended_budget_estimated_change_weekly_interactions,\n customer.currency_code\n FROM campaign_budget\n ${where}\n ORDER BY campaign_budget.name ASC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n campaign_budget.resource_name,\n campaign_budget.id,\n campaign_budget.name,\n campaign_budget.status,\n campaign_budget.delivery_method,\n campaign_budget.period,\n campaign_budget.amount_micros,\n customer.currency_code\n FROM campaign_budget\n ${where}\n ORDER BY campaign_budget.name ASC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"campaign_budget_with_recommendations\", gaql: richGaql, failureWarning: \"Campaign budget query with recommended budget fields failed\" },\n { label: \"campaign_budget_minimal\", gaql: fallbackGaql },\n ]);\n\n return ok({ budgets: result.rows, count: result.rows.length, gaql: result.gaql, warnings: result.warnings });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 15. google_ads_get_bidding_strategies ─────────────────────────\n server.tool(\n \"google_ads_get_bidding_strategies\",\n \"List portfolio bidding strategies. Includes metrics only when startDate/endDate are provided; otherwise returns structure only.\",\n {\n customerId: customerIdSchema,\n startDate: isoDateSchema.optional().describe(\"Optional start date YYYY-MM-DD for metrics\"),\n endDate: isoDateSchema.optional().describe(\"Optional end date YYYY-MM-DD for metrics\"),\n typeFilter: gaqlEnumSchema.optional().describe(\"Optional BiddingStrategyType enum filter\"),\n limit: z.number().int().min(1).max(1000).optional().default(1000),\n },\n async ({ customerId, startDate, endDate, typeFilter, limit }) => {\n try {\n const warnings: string[] = [];\n const where = optionalDateRange(startDate, endDate);\n if (typeFilter) where.push(`bidding_strategy.type = '${typeFilter}'`);\n\n const metricsFields = startDate || endDate\n ? `,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value`\n : \"\";\n if (!startDate && !endDate) {\n warnings.push(\"No date range provided; metrics were omitted and only bidding strategy structure was returned.\");\n }\n\n const gaql = oneLineGaql(`\n SELECT\n bidding_strategy.resource_name,\n bidding_strategy.id,\n bidding_strategy.name,\n bidding_strategy.status,\n bidding_strategy.type,\n bidding_strategy.currency_code,\n bidding_strategy.effective_currency_code,\n bidding_strategy.campaign_count\n ${metricsFields}\n FROM bidding_strategy\n ${buildWhere(where)}\n ORDER BY bidding_strategy.name ASC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n bidding_strategy.resource_name,\n bidding_strategy.id,\n bidding_strategy.name,\n bidding_strategy.status,\n bidding_strategy.type,\n bidding_strategy.currency_code,\n bidding_strategy.effective_currency_code,\n bidding_strategy.campaign_count\n FROM bidding_strategy\n ${typeFilter ? buildWhere([`bidding_strategy.type = '${typeFilter}'`]) : \"\"}\n ORDER BY bidding_strategy.name ASC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"bidding_strategy_requested\", gaql, failureWarning: \"Bidding strategy query with requested fields failed\" },\n { label: \"bidding_strategy_structure\", gaql: fallbackGaql },\n ]);\n\n return ok({ biddingStrategies: result.rows, count: result.rows.length, gaql: result.gaql, warnings: [...warnings, ...result.warnings] });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 16. google_ads_get_search_terms ───────────────────────────────\n server.tool(\n \"google_ads_get_search_terms\",\n \"Fetch search term performance from search_term_view or campaign_search_term_insight depending on reportType.\",\n {\n customerId: customerIdSchema,\n reportType: z.enum([\"search_term_view\", \"campaign_search_term_insight\"]).optional().default(\"search_term_view\"),\n startDate: isoDateSchema.describe(\"Start date YYYY-MM-DD\"),\n endDate: isoDateSchema.describe(\"End date YYYY-MM-DD\"),\n campaignId: numericIdSchema.optional().describe(\"Optional campaign ID filter\"),\n adGroupId: numericIdSchema.optional().describe(\"Optional ad group ID filter. Applies directly to search_term_view and via segments.ad_group for insight reports.\"),\n searchTermContains: z.string().min(1).optional().describe(\"Optional substring filter for search_term_view\"),\n limit: z.number().int().min(1).max(10000).optional().default(1000),\n },\n async ({ customerId, reportType, startDate, endDate, campaignId, adGroupId, searchTermContains, limit }) => {\n try {\n const where = requireDateRange(startDate, endDate);\n const cleanCustomerId = stripCustomerId(customerId);\n const warnings: string[] = [];\n\n let gaql: string;\n if (reportType === \"campaign_search_term_insight\") {\n if (campaignId) where.push(`campaign_search_term_insight.campaign_id = ${campaignId}`);\n if (adGroupId) where.push(`segments.ad_group = 'customers/${cleanCustomerId}/adGroups/${adGroupId}'`);\n if (searchTermContains) warnings.push(\"searchTermContains is ignored for campaign_search_term_insight because this report exposes category labels, not raw terms.\");\n\n gaql = oneLineGaql(`\n SELECT\n campaign_search_term_insight.resource_name,\n campaign_search_term_insight.id,\n campaign_search_term_insight.campaign_id,\n campaign_search_term_insight.category_label,\n segments.ad_group,\n campaign.id,\n campaign.name,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value,\n metrics.ctr\n FROM campaign_search_term_insight\n ${buildWhere(where)}\n ORDER BY metrics.impressions DESC\n LIMIT ${limit}\n `);\n } else {\n if (campaignId) where.push(`campaign.id = ${campaignId}`);\n if (adGroupId) where.push(`ad_group.id = ${adGroupId}`);\n if (searchTermContains) where.push(`search_term_view.search_term LIKE '%${quoteGaqlString(searchTermContains)}%'`);\n\n gaql = oneLineGaql(`\n SELECT\n search_term_view.resource_name,\n search_term_view.search_term,\n search_term_view.status,\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value,\n metrics.ctr,\n metrics.average_cpc\n FROM search_term_view\n ${buildWhere(where)}\n ORDER BY metrics.impressions DESC\n LIMIT ${limit}\n `);\n }\n\n const rows = await client.searchStream(customerId, gaql);\n return ok({ searchTerms: rows, reportType, count: rows.length, gaql, warnings });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 17. google_ads_get_landing_pages ──────────────────────────────\n server.tool(\n \"google_ads_get_landing_pages\",\n \"Fetch landing_page_view performance with final URL, campaign/ad group context, traffic, conversion, and landing-page quality metrics when supported.\",\n {\n customerId: customerIdSchema,\n startDate: isoDateSchema.describe(\"Start date YYYY-MM-DD\"),\n endDate: isoDateSchema.describe(\"End date YYYY-MM-DD\"),\n campaignId: numericIdSchema.optional().describe(\"Optional campaign ID filter\"),\n limit: z.number().int().min(1).max(10000).optional().default(1000),\n },\n async ({ customerId, startDate, endDate, campaignId, limit }) => {\n try {\n const where = requireDateRange(startDate, endDate);\n if (campaignId) where.push(`campaign.id = ${campaignId}`);\n\n const richGaql = oneLineGaql(`\n SELECT\n landing_page_view.resource_name,\n landing_page_view.unexpanded_final_url,\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value,\n metrics.all_conversions,\n metrics.mobile_friendly_clicks_percentage,\n metrics.valid_accelerated_mobile_pages_clicks_percentage,\n metrics.speed_score\n FROM landing_page_view\n ${buildWhere(where)}\n ORDER BY metrics.clicks DESC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n landing_page_view.resource_name,\n landing_page_view.unexpanded_final_url,\n campaign.id,\n campaign.name,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value\n FROM landing_page_view\n ${buildWhere(where)}\n ORDER BY metrics.clicks DESC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"landing_page_with_quality_metrics\", gaql: richGaql, failureWarning: \"Landing page query with quality metrics failed\" },\n { label: \"landing_page_minimal\", gaql: fallbackGaql },\n ]);\n\n return ok({ landingPages: result.rows, count: result.rows.length, gaql: result.gaql, warnings: result.warnings });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 18. google_ads_get_pmax_assets ────────────────────────────────\n server.tool(\n \"google_ads_get_pmax_assets\",\n \"List Performance Max asset group assets from asset_group_asset with asset group/campaign context and optional date-range performance metrics.\",\n {\n customerId: customerIdSchema,\n startDate: isoDateSchema.optional().describe(\"Optional start date YYYY-MM-DD for performance metrics\"),\n endDate: isoDateSchema.optional().describe(\"Optional end date YYYY-MM-DD for performance metrics\"),\n campaignId: numericIdSchema.optional().describe(\"Optional Performance Max campaign ID filter\"),\n assetGroupId: numericIdSchema.optional().describe(\"Optional asset group ID filter\"),\n fieldType: gaqlEnumSchema.optional().describe(\"Optional AssetFieldType enum filter, e.g. HEADLINE, LONG_HEADLINE, MARKETING_IMAGE\"),\n statusFilter: z.enum([\"ENABLED\", \"PAUSED\", \"REMOVED\"]).optional(),\n limit: z.number().int().min(1).max(10000).optional().default(1000),\n },\n async ({ customerId, startDate, endDate, campaignId, assetGroupId, fieldType, statusFilter, limit }) => {\n try {\n const warnings: string[] = [];\n const where = [\"campaign.advertising_channel_type = 'PERFORMANCE_MAX'\", ...optionalDateRange(startDate, endDate)];\n if (campaignId) where.push(`campaign.id = ${campaignId}`);\n if (assetGroupId) where.push(`asset_group.id = ${assetGroupId}`);\n if (fieldType) where.push(`asset_group_asset.field_type = '${fieldType}'`);\n if (statusFilter) where.push(`asset_group_asset.status = '${statusFilter}'`);\n if (!startDate && !endDate) warnings.push(\"No date range provided; performance metrics were omitted and only asset structure was returned.\");\n\n const metricsFields = startDate || endDate\n ? `,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value`\n : \"\";\n\n const richGaql = oneLineGaql(`\n SELECT\n campaign.id,\n campaign.name,\n asset_group.id,\n asset_group.name,\n asset_group.status,\n asset_group_asset.resource_name,\n asset_group_asset.asset,\n asset_group_asset.asset_group,\n asset_group_asset.field_type,\n asset_group_asset.source,\n asset_group_asset.status,\n asset.resource_name,\n asset.id,\n asset.name,\n asset.type,\n asset.text_asset.text,\n asset.image_asset.full_size.url,\n asset.youtube_video_asset.youtube_video_id,\n asset.youtube_video_asset.youtube_video_title\n ${metricsFields}\n FROM asset_group_asset\n ${buildWhere(where)}\n ORDER BY campaign.name ASC, asset_group.name ASC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n campaign.id,\n campaign.name,\n asset_group.id,\n asset_group.name,\n asset_group.status,\n asset_group_asset.resource_name,\n asset_group_asset.asset,\n asset_group_asset.asset_group,\n asset_group_asset.field_type,\n asset_group_asset.source,\n asset_group_asset.status\n ${metricsFields}\n FROM asset_group_asset\n ${buildWhere(where)}\n ORDER BY campaign.name ASC, asset_group.name ASC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"pmax_assets_with_asset_details\", gaql: richGaql, failureWarning: \"PMax asset query with asset details failed\" },\n { label: \"pmax_assets_minimal\", gaql: fallbackGaql },\n ]);\n\n return ok({ pmaxAssets: result.rows, count: result.rows.length, gaql: result.gaql, warnings: [...warnings, ...result.warnings] });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 19. google_ads_get_simulations ────────────────────────────────\n server.tool(\n \"google_ads_get_simulations\",\n \"Read-only planning/forecast query for campaign, ad group, or portfolio bidding simulations. Returns simulation metadata and projected point lists when available, with a metadata fallback.\",\n {\n customerId: customerIdSchema,\n level: z.enum([\"campaign\", \"ad_group\", \"bidding_strategy\"]).optional().default(\"campaign\").describe(\"Simulation resource level to query\"),\n campaignId: numericIdSchema.optional().describe(\"Optional campaign ID filter. Applies directly to campaign simulations and as campaign context for ad group simulations.\"),\n adGroupId: numericIdSchema.optional().describe(\"Optional ad group ID filter for ad_group simulations\"),\n biddingStrategyId: numericIdSchema.optional().describe(\"Optional portfolio bidding strategy ID filter for bidding_strategy simulations\"),\n simulationStartDate: isoDateSchema.optional().describe(\"Optional minimum simulation start date YYYY-MM-DD\"),\n simulationEndDate: isoDateSchema.optional().describe(\"Optional maximum simulation end date YYYY-MM-DD\"),\n typeFilter: gaqlEnumSchema.optional().describe(\"Optional SimulationType enum, e.g. BUDGET, TARGET_CPA, TARGET_ROAS, CPC_BID\"),\n modificationMethod: gaqlEnumSchema.optional().describe(\"Optional SimulationModificationMethod enum, e.g. UNIFORM, SCALING, DEFAULT\"),\n limit: z.number().int().min(1).max(1000).optional().default(1000),\n },\n async ({ customerId, level, campaignId, adGroupId, biddingStrategyId, simulationStartDate, simulationEndDate, typeFilter, modificationMethod, limit }) => {\n try {\n const warnings: string[] = [];\n if (simulationStartDate && simulationEndDate && parseIsoDate(simulationStartDate) > parseIsoDate(simulationEndDate)) {\n throw new Error(\"simulationStartDate must be on or before simulationEndDate.\");\n }\n\n let resource: string;\n let richFields: string;\n let fallbackFields: string;\n const where: string[] = [];\n\n if (level === \"ad_group\") {\n resource = \"ad_group_simulation\";\n richFields = `\n ad_group_simulation.resource_name,\n ad_group_simulation.ad_group_id,\n ad_group_simulation.type,\n ad_group_simulation.modification_method,\n ad_group_simulation.start_date,\n ad_group_simulation.end_date,\n ad_group_simulation.cpc_bid_point_list.points,\n ad_group_simulation.cpv_bid_point_list.points,\n ad_group_simulation.target_cpa_point_list.points,\n ad_group_simulation.target_roas_point_list.points,\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name,\n ad_group.status\n `;\n fallbackFields = `\n ad_group_simulation.resource_name,\n ad_group_simulation.ad_group_id,\n ad_group_simulation.type,\n ad_group_simulation.modification_method,\n ad_group_simulation.start_date,\n ad_group_simulation.end_date,\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name\n `;\n if (campaignId) where.push(`campaign.id = ${campaignId}`);\n if (adGroupId) where.push(`ad_group_simulation.ad_group_id = ${adGroupId}`);\n if (biddingStrategyId) warnings.push(\"biddingStrategyId is ignored for ad_group simulations.\");\n } else if (level === \"bidding_strategy\") {\n resource = \"bidding_strategy_simulation\";\n richFields = `\n bidding_strategy_simulation.resource_name,\n bidding_strategy_simulation.bidding_strategy_id,\n bidding_strategy_simulation.type,\n bidding_strategy_simulation.modification_method,\n bidding_strategy_simulation.start_date,\n bidding_strategy_simulation.end_date,\n bidding_strategy_simulation.target_cpa_point_list.points,\n bidding_strategy_simulation.target_roas_point_list.points,\n bidding_strategy.id,\n bidding_strategy.name,\n bidding_strategy.status,\n bidding_strategy.type\n `;\n fallbackFields = `\n bidding_strategy_simulation.resource_name,\n bidding_strategy_simulation.bidding_strategy_id,\n bidding_strategy_simulation.type,\n bidding_strategy_simulation.modification_method,\n bidding_strategy_simulation.start_date,\n bidding_strategy_simulation.end_date,\n bidding_strategy.id,\n bidding_strategy.name\n `;\n if (biddingStrategyId) where.push(`bidding_strategy_simulation.bidding_strategy_id = ${biddingStrategyId}`);\n if (campaignId) warnings.push(\"campaignId is ignored for bidding_strategy simulations.\");\n if (adGroupId) warnings.push(\"adGroupId is ignored for bidding_strategy simulations.\");\n } else {\n resource = \"campaign_simulation\";\n richFields = `\n campaign_simulation.resource_name,\n campaign_simulation.campaign_id,\n campaign_simulation.type,\n campaign_simulation.modification_method,\n campaign_simulation.start_date,\n campaign_simulation.end_date,\n campaign_simulation.budget_point_list.points,\n campaign_simulation.cpc_bid_point_list.points,\n campaign_simulation.target_cpa_point_list.points,\n campaign_simulation.target_impression_share_point_list.points,\n campaign_simulation.target_roas_point_list.points,\n campaign.id,\n campaign.name,\n campaign.status,\n campaign.advertising_channel_type,\n campaign.bidding_strategy_type\n `;\n fallbackFields = `\n campaign_simulation.resource_name,\n campaign_simulation.campaign_id,\n campaign_simulation.type,\n campaign_simulation.modification_method,\n campaign_simulation.start_date,\n campaign_simulation.end_date,\n campaign.id,\n campaign.name\n `;\n if (campaignId) where.push(`campaign_simulation.campaign_id = ${campaignId}`);\n if (adGroupId) warnings.push(\"adGroupId is ignored for campaign simulations.\");\n if (biddingStrategyId) warnings.push(\"biddingStrategyId is ignored for campaign simulations.\");\n }\n\n if (simulationStartDate) where.push(`${resource}.start_date >= '${simulationStartDate}'`);\n if (simulationEndDate) where.push(`${resource}.end_date <= '${simulationEndDate}'`);\n if (typeFilter) where.push(`${resource}.type = '${typeFilter}'`);\n if (modificationMethod) where.push(`${resource}.modification_method = '${modificationMethod}'`);\n\n const richGaql = oneLineGaql(`\n SELECT\n ${richFields}\n FROM ${resource}\n ${buildWhere(where)}\n ORDER BY ${resource}.start_date DESC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n ${fallbackFields}\n FROM ${resource}\n ${buildWhere(where)}\n ORDER BY ${resource}.start_date DESC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: `${resource}_with_points`, gaql: richGaql, failureWarning: \"Simulation query with projected point lists failed\" },\n { label: `${resource}_metadata`, gaql: fallbackGaql },\n ]);\n\n return ok({ simulations: result.rows, level, count: result.rows.length, gaql: result.gaql, warnings: [...warnings, ...result.warnings] });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 20. google_ads_get_paid_organic_search_terms ──────────────────\n server.tool(\n \"google_ads_get_paid_organic_search_terms\",\n \"Read-only paid/organic search terms report. Uses paid_organic_search_term_view when available and falls back to paid-only search_term_view if organic fields are unavailable.\",\n {\n customerId: customerIdSchema,\n startDate: isoDateSchema.describe(\"Start date YYYY-MM-DD\"),\n endDate: isoDateSchema.describe(\"End date YYYY-MM-DD\"),\n campaignId: numericIdSchema.optional().describe(\"Optional campaign ID filter\"),\n adGroupId: numericIdSchema.optional().describe(\"Optional ad group ID filter\"),\n searchTermContains: z.string().min(1).optional().describe(\"Optional search term substring filter\"),\n serpType: z.enum([\"ADS_AND_ORGANIC\", \"ADS_ONLY\", \"ORGANIC_ONLY\", \"UNKNOWN\", \"UNSPECIFIED\"]).optional().describe(\"Optional search engine results page type segment filter\"),\n limit: z.number().int().min(1).max(10000).optional().default(1000),\n },\n async ({ customerId, startDate, endDate, campaignId, adGroupId, searchTermContains, serpType, limit }) => {\n try {\n const paidOrganicWhere = requireDateRange(startDate, endDate);\n if (campaignId) paidOrganicWhere.push(`campaign.id = ${campaignId}`);\n if (adGroupId) paidOrganicWhere.push(`ad_group.id = ${adGroupId}`);\n if (searchTermContains) paidOrganicWhere.push(`paid_organic_search_term_view.search_term LIKE '%${quoteGaqlString(searchTermContains)}%'`);\n if (serpType) paidOrganicWhere.push(`segments.search_engine_results_page_type = '${serpType}'`);\n\n const fallbackWhere = requireDateRange(startDate, endDate);\n if (campaignId) fallbackWhere.push(`campaign.id = ${campaignId}`);\n if (adGroupId) fallbackWhere.push(`ad_group.id = ${adGroupId}`);\n if (searchTermContains) fallbackWhere.push(`search_term_view.search_term LIKE '%${quoteGaqlString(searchTermContains)}%'`);\n\n const richGaql = oneLineGaql(`\n SELECT\n paid_organic_search_term_view.resource_name,\n paid_organic_search_term_view.search_term,\n segments.search_engine_results_page_type,\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name,\n metrics.impressions,\n metrics.clicks,\n metrics.ctr,\n metrics.average_cpc,\n metrics.combined_clicks,\n metrics.combined_clicks_per_query,\n metrics.combined_queries,\n metrics.organic_clicks,\n metrics.organic_clicks_per_query,\n metrics.organic_impressions,\n metrics.organic_impressions_per_query,\n metrics.organic_queries\n FROM paid_organic_search_term_view\n ${buildWhere(paidOrganicWhere)}\n ORDER BY metrics.combined_clicks DESC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n search_term_view.resource_name,\n search_term_view.search_term,\n search_term_view.status,\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name,\n metrics.impressions,\n metrics.clicks,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value\n FROM search_term_view\n ${buildWhere(fallbackWhere)}\n ORDER BY metrics.clicks DESC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"paid_organic_search_term_view\", gaql: richGaql, failureWarning: \"Paid/organic search term query failed; organic reporting may require eligible linked organic search data or compatible fields\" },\n { label: \"search_term_view_paid_only\", gaql: fallbackGaql },\n ]);\n\n return ok({ searchTerms: result.rows, source: result.queryLabel, count: result.rows.length, gaql: result.gaql, warnings: result.warnings });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 21. google_ads_get_shopping_products ──────────────────────────\n server.tool(\n \"google_ads_get_shopping_products\",\n \"Read-only Merchant Center product catalog/eligibility report via shopping_product. Supports account, campaign, and ad group scopes with performance metrics when the account has Shopping/PMax e-commerce data.\",\n {\n customerId: customerIdSchema,\n scope: z.enum([\"account\", \"campaign\", \"ad_group\"]).optional().default(\"account\").describe(\"shopping_product scope. ad_group scope requires campaignId and adGroupId.\"),\n startDate: isoDateSchema.optional().describe(\"Optional start date YYYY-MM-DD. Used only as a WHERE filter; shopping_product cannot segment by date.\"),\n endDate: isoDateSchema.optional().describe(\"Optional end date YYYY-MM-DD. Used only as a WHERE filter; shopping_product cannot segment by date.\"),\n campaignId: numericIdSchema.optional().describe(\"Campaign ID for campaign or ad_group scope\"),\n adGroupId: numericIdSchema.optional().describe(\"Ad group ID for ad_group scope\"),\n merchantCenterId: numericIdSchema.optional().describe(\"Optional Merchant Center ID filter\"),\n itemId: z.string().min(1).optional().describe(\"Optional Merchant Center item ID filter\"),\n titleContains: z.string().min(1).optional().describe(\"Optional product title substring filter\"),\n feedLabel: z.string().min(1).optional().describe(\"Optional feed label filter\"),\n statusFilter: z.enum([\"ELIGIBLE\", \"ELIGIBLE_LIMITED\", \"NOT_ELIGIBLE\", \"UNKNOWN\", \"UNSPECIFIED\"]).optional(),\n limit: z.number().int().min(1).max(10000).optional().default(1000),\n },\n async ({ customerId, scope, startDate, endDate, campaignId, adGroupId, merchantCenterId, itemId, titleContains, feedLabel, statusFilter, limit }) => {\n try {\n const warnings: string[] = [];\n const cleanCustomerId = stripCustomerId(customerId);\n const where = optionalDateRange(startDate, endDate);\n\n if (startDate || endDate) {\n warnings.push(\"shopping_product does not support date segmentation; segments.date is used only as a WHERE filter.\");\n }\n if (scope === \"campaign\" && !campaignId) throw new Error(\"campaignId is required when scope is campaign.\");\n if (scope === \"ad_group\" && (!campaignId || !adGroupId)) throw new Error(\"campaignId and adGroupId are required when scope is ad_group.\");\n if (scope === \"account\" && (campaignId || adGroupId)) warnings.push(\"campaignId/adGroupId were provided with account scope; use scope campaign or ad_group to constrain product inclusion.\");\n\n if (scope === \"campaign\" || scope === \"ad_group\") {\n where.push(`shopping_product.campaign = 'customers/${cleanCustomerId}/campaigns/${campaignId}'`);\n }\n if (scope === \"ad_group\") {\n where.push(`shopping_product.ad_group = 'customers/${cleanCustomerId}/adGroups/${adGroupId}'`);\n }\n if (merchantCenterId) where.push(`shopping_product.merchant_center_id = ${merchantCenterId}`);\n if (itemId) where.push(`shopping_product.item_id = '${quoteGaqlString(itemId)}'`);\n if (titleContains) where.push(`shopping_product.title LIKE '%${quoteGaqlString(titleContains)}%'`);\n if (feedLabel) where.push(`shopping_product.feed_label = '${quoteGaqlString(feedLabel)}'`);\n if (statusFilter) where.push(`shopping_product.status = '${statusFilter}'`);\n\n const contextFields = scope === \"account\"\n ? \"\"\n : `,\n campaign.id,\n campaign.name${scope === \"ad_group\" ? `,\n ad_group.id,\n ad_group.name` : \"\"}`;\n\n const richGaql = oneLineGaql(`\n SELECT\n shopping_product.resource_name,\n shopping_product.merchant_center_id,\n shopping_product.multi_client_account_id,\n shopping_product.item_id,\n shopping_product.title,\n shopping_product.brand,\n shopping_product.channel,\n shopping_product.channel_exclusivity,\n shopping_product.condition,\n shopping_product.availability,\n shopping_product.status,\n shopping_product.feed_label,\n shopping_product.language_code,\n shopping_product.target_countries,\n shopping_product.currency_code,\n shopping_product.price_micros,\n shopping_product.product_image_uri,\n shopping_product.category_level1,\n shopping_product.category_level2,\n shopping_product.product_type_level1,\n shopping_product.product_type_level2,\n shopping_product.custom_attribute0,\n shopping_product.issues,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value,\n metrics.ctr,\n metrics.orders,\n metrics.revenue_micros,\n metrics.units_sold,\n metrics.gross_profit_micros\n ${contextFields}\n FROM shopping_product\n ${buildWhere(where)}\n ORDER BY metrics.impressions DESC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n shopping_product.resource_name,\n shopping_product.merchant_center_id,\n shopping_product.item_id,\n shopping_product.title,\n shopping_product.brand,\n shopping_product.status,\n shopping_product.feed_label,\n shopping_product.currency_code,\n shopping_product.price_micros\n ${contextFields}\n FROM shopping_product\n ${buildWhere(where)}\n ORDER BY shopping_product.title ASC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"shopping_product_with_metrics_and_issues\", gaql: richGaql, failureWarning: \"Shopping product query with performance/cart/issue fields failed\" },\n { label: \"shopping_product_catalog\", gaql: fallbackGaql },\n ]);\n\n return ok({ shoppingProducts: result.rows, scope, count: result.rows.length, gaql: result.gaql, warnings: [...warnings, ...result.warnings] });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 22. google_ads_get_shopping_performance ───────────────────────\n server.tool(\n \"google_ads_get_shopping_performance\",\n \"Read-only Shopping performance report keyed by Merchant Center product dimensions. Useful for joining spend/conversions to merchant ID, item ID, title, brand, feed label, and custom labels.\",\n {\n customerId: customerIdSchema,\n startDate: isoDateSchema.describe(\"Start date YYYY-MM-DD\"),\n endDate: isoDateSchema.describe(\"End date YYYY-MM-DD\"),\n campaignId: numericIdSchema.optional().describe(\"Optional campaign ID filter\"),\n adGroupId: numericIdSchema.optional().describe(\"Optional ad group ID filter\"),\n merchantCenterId: numericIdSchema.optional().describe(\"Optional Merchant Center ID filter\"),\n itemId: z.string().min(1).optional().describe(\"Optional Merchant Center item ID filter\"),\n titleContains: z.string().min(1).optional().describe(\"Optional product title substring filter\"),\n brand: z.string().min(1).optional().describe(\"Optional product brand filter\"),\n feedLabel: z.string().min(1).optional().describe(\"Optional product feed label filter\"),\n limit: z.number().int().min(1).max(10000).optional().default(1000),\n },\n async ({ customerId, startDate, endDate, campaignId, adGroupId, merchantCenterId, itemId, titleContains, brand, feedLabel, limit }) => {\n try {\n const where = requireDateRange(startDate, endDate);\n if (campaignId) where.push(`campaign.id = ${campaignId}`);\n if (adGroupId) where.push(`ad_group.id = ${adGroupId}`);\n if (merchantCenterId) where.push(`segments.product_merchant_id = ${merchantCenterId}`);\n if (itemId) where.push(`segments.product_item_id = '${quoteGaqlString(itemId)}'`);\n if (titleContains) where.push(`segments.product_title LIKE '%${quoteGaqlString(titleContains)}%'`);\n if (brand) where.push(`segments.product_brand = '${quoteGaqlString(brand)}'`);\n if (feedLabel) where.push(`segments.product_feed_label = '${quoteGaqlString(feedLabel)}'`);\n\n const baseFields = `\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name,\n segments.product_merchant_id,\n segments.product_item_id,\n segments.product_title,\n segments.product_brand,\n segments.product_channel,\n segments.product_condition,\n segments.product_feed_label,\n segments.product_custom_attribute0,\n segments.product_custom_attribute1,\n segments.product_custom_attribute2,\n segments.product_custom_attribute3,\n segments.product_custom_attribute4,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value,\n metrics.ctr,\n metrics.average_cpc\n `;\n\n const richGaql = oneLineGaql(`\n SELECT\n ${baseFields},\n metrics.all_conversions,\n metrics.orders,\n metrics.revenue_micros,\n metrics.units_sold,\n metrics.gross_profit_micros,\n metrics.cost_of_goods_sold_micros,\n metrics.average_order_value_micros\n FROM shopping_performance_view\n ${buildWhere(where)}\n ORDER BY metrics.cost_micros DESC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n ${baseFields}\n FROM shopping_performance_view\n ${buildWhere(where)}\n ORDER BY metrics.cost_micros DESC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"shopping_performance_with_cart_metrics\", gaql: richGaql, failureWarning: \"Shopping performance query with cart/profit metrics failed\" },\n { label: \"shopping_performance_core\", gaql: fallbackGaql },\n ]);\n\n return ok({ shoppingPerformance: result.rows, count: result.rows.length, gaql: result.gaql, warnings: result.warnings });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 23. google_ads_get_pmax_placements ────────────────────────────\n server.tool(\n \"google_ads_get_pmax_placements\",\n \"Read-only Performance Max placement diagnostics from performance_max_placement_view. Returns placement type, display name, target URL, campaign context, and impressions only.\",\n {\n customerId: customerIdSchema,\n startDate: isoDateSchema.describe(\"Start date YYYY-MM-DD\"),\n endDate: isoDateSchema.describe(\"End date YYYY-MM-DD\"),\n campaignId: numericIdSchema.optional().describe(\"Optional Performance Max campaign ID filter\"),\n placementType: z.enum([\"WEBSITE\", \"MOBILE_APPLICATION\", \"YOUTUBE_VIDEO\", \"YOUTUBE_CHANNEL\", \"GOOGLE_PRODUCTS\", \"UNKNOWN\", \"UNSPECIFIED\"]).optional().describe(\"Optional PlacementType enum filter\"),\n placementContains: z.string().min(1).optional().describe(\"Optional substring filter for the placement string\"),\n limit: z.number().int().min(1).max(10000).optional().default(1000),\n },\n async ({ customerId, startDate, endDate, campaignId, placementType, placementContains, limit }) => {\n try {\n const warnings = [\"performance_max_placement_view exposes impression metrics only; clicks/cost/conversions are not available on this resource.\"];\n const where = requireDateRange(startDate, endDate);\n if (campaignId) where.push(`campaign.id = ${campaignId}`);\n if (placementType) where.push(`performance_max_placement_view.placement_type = '${placementType}'`);\n if (placementContains) where.push(`performance_max_placement_view.placement LIKE '%${quoteGaqlString(placementContains)}%'`);\n\n const richGaql = oneLineGaql(`\n SELECT\n performance_max_placement_view.resource_name,\n performance_max_placement_view.display_name,\n performance_max_placement_view.placement,\n performance_max_placement_view.placement_type,\n performance_max_placement_view.target_url,\n campaign.id,\n campaign.name,\n metrics.impressions\n FROM performance_max_placement_view\n ${buildWhere(where)}\n ORDER BY metrics.impressions DESC\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n performance_max_placement_view.resource_name,\n performance_max_placement_view.display_name,\n performance_max_placement_view.placement,\n performance_max_placement_view.placement_type,\n metrics.impressions\n FROM performance_max_placement_view\n ${buildWhere(where)}\n ORDER BY metrics.impressions DESC\n LIMIT ${limit}\n `);\n\n const result = await runGaqlWithFallback(client, customerId, [\n { label: \"pmax_placements_with_target_url_and_campaign\", gaql: richGaql, failureWarning: \"PMax placement query with target URL/campaign context failed\" },\n { label: \"pmax_placements_core\", gaql: fallbackGaql },\n ]);\n\n return ok({\n placements: result.rows,\n pmaxPlacements: result.rows,\n count: result.rows.length,\n gaql: result.gaql,\n warnings: [...warnings, ...result.warnings],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // ── 24. google_ads_get_pmax_asset_diagnostics ─────────────────────\n server.tool(\n \"google_ads_get_pmax_asset_diagnostics\",\n \"Read-only Performance Max asset group diagnostics. Returns ad strength, asset coverage action items, primary status reasons, optional performance metrics, and optional top asset combinations.\",\n {\n customerId: customerIdSchema,\n startDate: isoDateSchema.optional().describe(\"Optional start date YYYY-MM-DD for asset group metrics/top combinations\"),\n endDate: isoDateSchema.optional().describe(\"Optional end date YYYY-MM-DD for asset group metrics/top combinations\"),\n campaignId: numericIdSchema.optional().describe(\"Optional Performance Max campaign ID filter\"),\n assetGroupId: numericIdSchema.optional().describe(\"Optional asset group ID filter\"),\n statusFilter: z.enum([\"ENABLED\", \"PAUSED\", \"REMOVED\"]).optional(),\n includeTopCombinations: z.boolean().optional().default(true).describe(\"Also query asset_group_top_combination_view when available\"),\n limit: z.number().int().min(1).max(1000).optional().default(1000),\n },\n async ({ customerId, startDate, endDate, campaignId, assetGroupId, statusFilter, includeTopCombinations, limit }) => {\n try {\n const warnings: string[] = [];\n const where = [\"campaign.advertising_channel_type = 'PERFORMANCE_MAX'\", ...optionalDateRange(startDate, endDate)];\n if (campaignId) where.push(`campaign.id = ${campaignId}`);\n if (assetGroupId) where.push(`asset_group.id = ${assetGroupId}`);\n if (statusFilter) where.push(`asset_group.status = '${statusFilter}'`);\n if (!startDate && !endDate) warnings.push(\"No date range provided; performance metrics were omitted and only asset group diagnostics were returned.\");\n\n const metricsFields = startDate || endDate\n ? `,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.conversions_value,\n metrics.ctr`\n : \"\";\n const assetGroupOrderBy = startDate || endDate\n ? \"metrics.impressions DESC\"\n : \"campaign.name ASC, asset_group.name ASC\";\n\n const richGaql = oneLineGaql(`\n SELECT\n campaign.id,\n campaign.name,\n campaign.status,\n asset_group.resource_name,\n asset_group.id,\n asset_group.name,\n asset_group.status,\n asset_group.primary_status,\n asset_group.primary_status_reasons,\n asset_group.ad_strength,\n asset_group.asset_coverage.ad_strength_action_items,\n asset_group.final_urls,\n asset_group.final_mobile_urls\n ${metricsFields}\n FROM asset_group\n ${buildWhere(where)}\n ORDER BY ${assetGroupOrderBy}\n LIMIT ${limit}\n `);\n const fallbackGaql = oneLineGaql(`\n SELECT\n campaign.id,\n campaign.name,\n asset_group.resource_name,\n asset_group.id,\n asset_group.name,\n asset_group.status,\n asset_group.primary_status,\n asset_group.primary_status_reasons,\n asset_group.ad_strength\n ${metricsFields}\n FROM asset_group\n ${buildWhere(where)}\n ORDER BY ${assetGroupOrderBy}\n LIMIT ${limit}\n `);\n\n const assetGroups = await runGaqlWithFallback(client, customerId, [\n { label: \"pmax_asset_group_diagnostics_with_coverage\", gaql: richGaql, failureWarning: \"PMax asset group query with asset coverage/final URL fields failed\" },\n { label: \"pmax_asset_group_diagnostics_core\", gaql: fallbackGaql },\n ]);\n\n let topCombinations: GoogleAdsRow[] = [];\n let topCombinationsGaql: string | null = null;\n if (includeTopCombinations) {\n const topCombinationWhere = optionalDateRange(startDate, endDate);\n if (campaignId) topCombinationWhere.push(`campaign.id = ${campaignId}`);\n if (assetGroupId) topCombinationWhere.push(`asset_group.id = ${assetGroupId}`);\n\n topCombinationsGaql = oneLineGaql(`\n SELECT\n asset_group_top_combination_view.resource_name,\n asset_group_top_combination_view.asset_group_top_combinations,\n campaign.id,\n campaign.name,\n asset_group.id,\n asset_group.name\n FROM asset_group_top_combination_view\n ${buildWhere(topCombinationWhere)}\n LIMIT ${limit}\n `);\n\n try {\n topCombinations = await client.searchStream(customerId, topCombinationsGaql);\n } catch (error) {\n warnings.push(`PMax top combinations query failed; asset_group_top_combination_view may be unavailable for this account/API combination: ${getErrorMessage(error)}`);\n }\n }\n\n return ok({\n assetGroups: assetGroups.rows,\n assetGroupCount: assetGroups.rows.length,\n topCombinations,\n topCombinationCount: topCombinations.length,\n gaql: {\n assetGroups: assetGroups.gaql,\n topCombinations: topCombinationsGaql,\n },\n warnings: [...warnings, ...assetGroups.warnings],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n registerGoogleAdsKeywordPlannerTools(server, client, ok);\n registerGoogleAdsDiscoveryTools(server, client, ok);\n registerGoogleAdsReadOnlyRpcTool(server, client, ok);\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ADS API v23 TYPES FOR MCP SERVER\n// Complete TypeScript interfaces for Google Ads integration\n// Type definitions for the Google Ads API surface used by this server\n// ============================================\n\n// ============================================\n// CORE ENUMS & TYPE ALIASES\n// ============================================\n\n/**\n * GAQL FROM resource types\n */\nexport type GoogleAdsResourceType =\n // Core\n | \"campaign\"\n | \"ad_group\"\n | \"ad_group_ad\"\n | \"customer\"\n // Search\n | \"keyword_view\"\n | \"search_term_view\"\n | \"dynamic_search_ads_search_term_view\"\n // Shopping\n | \"shopping_performance_view\"\n | \"shopping_product\"\n // PMax\n | \"asset_group\"\n | \"asset_group_asset\"\n | \"asset_group_listing_group_filter\"\n | \"asset_group_product_group_view\"\n | \"asset_group_signal\"\n | \"asset_group_top_combination_view\"\n | \"campaign_search_term_insight\"\n // Display & Placement\n | \"detail_placement_view\"\n | \"performance_max_placement_view\"\n | \"topic_view\"\n | \"display_keyword_view\"\n | \"managed_placement_view\"\n // Video\n | \"video\"\n // Geographic\n | \"geographic_view\"\n | \"user_location_view\"\n // Landing Pages\n | \"landing_page_view\"\n | \"expanded_landing_page_view\"\n // Audience\n | \"campaign_audience_view\"\n | \"ad_group_audience_view\"\n | \"age_range_view\"\n | \"gender_view\"\n | \"parental_status_view\"\n | \"income_range_view\"\n // Criterion & Targeting\n | \"campaign_criterion\"\n | \"ad_group_criterion\"\n // Assets & Creative\n | \"ad_group_ad_asset_view\"\n | \"campaign_asset\"\n | \"ad_group_asset\"\n // Budget & Bidding\n | \"campaign_budget\"\n | \"bidding_strategy\"\n | \"campaign_simulation\"\n | \"ad_group_simulation\"\n | \"bidding_strategy_simulation\"\n // Conversion & Tracking\n | \"conversion_action\"\n // Account & Organization\n | \"change_event\"\n | \"change_status\"\n | \"label\"\n | \"paid_organic_search_term_view\"\n | \"experiment\";\n\n/**\n * Metric categories for organization\n */\nexport type GoogleAdsMetricCategory =\n | \"core\"\n | \"spend\"\n | \"conversion\"\n | \"impression_share\"\n | \"video\"\n | \"engagement\"\n | \"shopping\"\n | \"competitive\"\n | \"quality\"\n | \"cross_device\"\n | \"invalid_traffic\"\n | \"calculated\";\n\n/**\n * Dimension categories\n */\nexport type GoogleAdsDimensionCategory =\n | \"entity\"\n | \"time\"\n | \"device\"\n | \"network\"\n | \"conversion\"\n | \"demographic\"\n | \"geographic\"\n | \"campaign_type\"\n | \"bidding\"\n | \"budget\"\n | \"ad_format\"\n | \"shopping\"\n | \"interaction\";\n\n/**\n * Metric format types for display\n */\nexport type GoogleAdsMetricFormat =\n | \"number\"\n | \"currency\"\n | \"micro_currency\"\n | \"percentage\"\n | \"ratio\"\n | \"string\"\n | \"duration\"\n | \"enum\";\n\n/**\n * Filter operator types for GAQL WHERE\n */\nexport type GoogleAdsFilterOperator =\n | \"=\"\n | \"!=\"\n | \">\"\n | \">=\"\n | \"<\"\n | \"<=\"\n | \"IN\"\n | \"NOT IN\"\n | \"LIKE\"\n | \"NOT LIKE\"\n | \"CONTAINS ANY\"\n | \"CONTAINS ALL\"\n | \"CONTAINS NONE\"\n | \"IS NULL\"\n | \"IS NOT NULL\"\n | \"BETWEEN\"\n | \"DURING\";\n\n/**\n * Date presets for GAQL DURING clause\n */\nexport type GoogleAdsDatePreset =\n | \"TODAY\"\n | \"YESTERDAY\"\n | \"LAST_7_DAYS\"\n | \"LAST_14_DAYS\"\n | \"LAST_30_DAYS\"\n | \"LAST_BUSINESS_WEEK\"\n | \"THIS_WEEK_MON_TODAY\"\n | \"THIS_WEEK_SUN_TODAY\"\n | \"LAST_WEEK_MON_SUN\"\n | \"LAST_WEEK_SUN_SAT\"\n | \"THIS_MONTH\"\n | \"LAST_MONTH\"\n | \"THIS_QUARTER\"\n | \"LAST_QUARTER\"\n | \"LAST_90_DAYS\";\n\n/**\n * Campaign status enum\n */\nexport type GoogleAdsCampaignStatus = \"ENABLED\" | \"PAUSED\" | \"REMOVED\" | \"UNKNOWN\" | \"UNSPECIFIED\";\n\n/**\n * Campaign types (advertising channel)\n */\nexport type GoogleAdsChannelType =\n | \"SEARCH\"\n | \"DISPLAY\"\n | \"SHOPPING\"\n | \"VIDEO\"\n | \"MULTI_CHANNEL\"\n | \"PERFORMANCE_MAX\"\n | \"DEMAND_GEN\"\n | \"TRAVEL\"\n | \"LOCAL\"\n | \"SMART\"\n | \"LOCAL_SERVICES\"\n | \"UNKNOWN\";\n\n/**\n * Bidding strategy types\n */\nexport type GoogleAdsBiddingStrategyType =\n | \"TARGET_CPA\"\n | \"TARGET_ROAS\"\n | \"MAXIMIZE_CONVERSIONS\"\n | \"MAXIMIZE_CONVERSION_VALUE\"\n | \"MANUAL_CPC\"\n | \"MANUAL_CPM\"\n | \"MANUAL_CPV\"\n | \"ENHANCED_CPC\"\n | \"TARGET_IMPRESSION_SHARE\"\n | \"TARGET_SPEND\"\n | \"COMMISSION\"\n | \"UNKNOWN\";\n\n// ============================================\n// AUTHENTICATION TYPES\n// ============================================\n\nexport interface GoogleAdsApiConfig {\n accessToken: string;\n developerToken: string;\n loginCustomerId?: string;\n}\n\n// ============================================\n// CUSTOMER (ACCOUNT) TYPES\n// ============================================\n\nexport interface GoogleAdsCustomer {\n id: string;\n descriptiveName: string;\n currencyCode: string;\n timeZone: string;\n manager: boolean;\n testAccount: boolean;\n resourceName: string;\n}\n\nexport interface GoogleAdsCustomerListResponse {\n resourceNames: string[];\n}\n\n// ============================================\n// GAQL RESPONSE TYPES\n// ============================================\n\n/**\n * Raw API row - deeply nested\n */\nexport interface GoogleAdsRow {\n campaign?: {\n resourceName?: string;\n id?: string;\n name?: string;\n status?: string;\n advertisingChannelType?: string;\n advertisingChannelSubType?: string;\n biddingStrategyType?: string;\n campaignBudget?: string;\n startDate?: string;\n endDate?: string;\n labels?: string[];\n [key: string]: unknown;\n };\n adGroup?: {\n resourceName?: string;\n id?: string;\n name?: string;\n status?: string;\n type?: string;\n [key: string]: unknown;\n };\n adGroupAd?: {\n resourceName?: string;\n ad?: {\n id?: string;\n type?: string;\n name?: string;\n finalUrls?: string[];\n [key: string]: unknown;\n };\n status?: string;\n adStrength?: string;\n [key: string]: unknown;\n };\n keywordView?: {\n resourceName?: string;\n [key: string]: unknown;\n };\n adGroupCriterion?: {\n resourceName?: string;\n criterionId?: string;\n keyword?: {\n text?: string;\n matchType?: string;\n };\n qualityInfo?: {\n qualityScore?: number;\n creativeQualityScore?: string;\n postClickQualityScore?: string;\n searchPredictedCtr?: string;\n };\n [key: string]: unknown;\n };\n metrics?: {\n impressions?: string;\n clicks?: string;\n costMicros?: string;\n ctr?: number;\n averageCpc?: string;\n averageCpm?: string;\n conversions?: number;\n conversionsValue?: number;\n costPerConversion?: string;\n conversionsFromInteractionsRate?: number;\n allConversions?: number;\n allConversionsValue?: number;\n costPerAllConversions?: string;\n viewThroughConversions?: string;\n crossDeviceConversions?: number;\n videoViews?: string;\n videoViewRate?: number;\n videoQuartileP25Rate?: number;\n videoQuartileP50Rate?: number;\n videoQuartileP75Rate?: number;\n videoQuartileP100Rate?: number;\n interactions?: string;\n interactionRate?: number;\n engagementRate?: number;\n searchImpressionShare?: number;\n searchBudgetLostImpressionShare?: number;\n searchRankLostImpressionShare?: number;\n searchTopImpressionShare?: number;\n searchAbsoluteTopImpressionShare?: number;\n searchExactMatchImpressionShare?: number;\n contentImpressionShare?: number;\n contentBudgetLostImpressionShare?: number;\n contentRankLostImpressionShare?: number;\n topImpressionPercentage?: number;\n absoluteTopImpressionPercentage?: number;\n invalidClicks?: string;\n invalidClickRate?: number;\n reach?: string;\n frequencyCount?: number;\n averageCost?: string;\n averageCpe?: string;\n averageCpv?: string;\n activeViewCpm?: string;\n activeViewCtr?: number;\n activeViewImpressions?: string;\n activeViewMeasurability?: number;\n activeViewMeasurableCostMicros?: string;\n activeViewMeasurableImpressions?: string;\n activeViewViewability?: number;\n [key: string]: unknown;\n };\n segments?: {\n date?: string;\n dayOfWeek?: string;\n hour?: number;\n month?: string;\n week?: string;\n quarter?: string;\n year?: number;\n device?: string;\n adNetworkType?: string;\n slot?: string;\n conversionAction?: string;\n conversionActionName?: string;\n conversionActionCategory?: string;\n externalConversionSource?: string;\n clickType?: string;\n adDestinationType?: string;\n ageRange?: string;\n gender?: string;\n geoTargetCountry?: string;\n geoTargetRegion?: string;\n geoTargetMetro?: string;\n geoTargetCity?: string;\n advertisingChannelType?: string;\n advertisingChannelSubType?: string;\n productItemId?: string;\n productBrand?: string;\n productCategoryLevel1?: string;\n productCategoryLevel2?: string;\n productTypeL1?: string;\n productCustomAttribute0?: string;\n keyword?: {\n info?: {\n text?: string;\n matchType?: string;\n };\n };\n [key: string]: unknown;\n };\n customer?: {\n resourceName?: string;\n id?: string;\n descriptiveName?: string;\n currencyCode?: string;\n timeZone?: string;\n manager?: boolean;\n testAccount?: boolean;\n [key: string]: unknown;\n };\n campaignBudget?: {\n resourceName?: string;\n id?: string;\n amountMicros?: string;\n period?: string;\n totalAmountMicros?: string;\n status?: string;\n deliveryMethod?: string;\n [key: string]: unknown;\n };\n biddingStrategy?: {\n resourceName?: string;\n id?: string;\n name?: string;\n type?: string;\n [key: string]: unknown;\n };\n conversionAction?: {\n resourceName?: string;\n id?: string;\n name?: string;\n category?: string;\n type?: string;\n status?: string;\n [key: string]: unknown;\n };\n geographicView?: {\n resourceName?: string;\n countryCriterionId?: string;\n locationType?: string;\n [key: string]: unknown;\n };\n shoppingPerformanceView?: {\n resourceName?: string;\n [key: string]: unknown;\n };\n searchTermView?: {\n resourceName?: string;\n searchTerm?: string;\n [key: string]: unknown;\n };\n landingPageView?: {\n resourceName?: string;\n unexpandedFinalUrl?: string;\n [key: string]: unknown;\n };\n video?: {\n resourceName?: string;\n id?: string;\n title?: string;\n channelId?: string;\n durationMillis?: string;\n [key: string]: unknown;\n };\n assetGroup?: {\n resourceName?: string;\n id?: string;\n name?: string;\n status?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n// ============================================\n// FLATTENED INSIGHT ROW (for display)\n// ============================================\n\nexport interface GoogleAdsInsightRow {\n // Entity fields\n \"campaign.id\"?: string;\n \"campaign.name\"?: string;\n \"campaign.status\"?: string;\n \"campaign.advertising_channel_type\"?: string;\n \"campaign.advertising_channel_sub_type\"?: string;\n \"campaign.bidding_strategy_type\"?: string;\n \"ad_group.id\"?: string;\n \"ad_group.name\"?: string;\n \"ad_group.status\"?: string;\n \"ad_group_ad.ad.id\"?: string;\n \"ad_group_ad.ad.name\"?: string;\n \"ad_group_ad.ad.type\"?: string;\n \"ad_group_criterion.keyword.text\"?: string;\n \"ad_group_criterion.keyword.match_type\"?: string;\n \"ad_group_criterion.quality_info.quality_score\"?: number;\n \"customer.id\"?: string;\n \"customer.descriptive_name\"?: string;\n\n // Metrics (converted from camelCase API response)\n \"metrics.impressions\"?: number;\n \"metrics.clicks\"?: number;\n \"metrics.cost_micros\"?: number;\n \"metrics.cost\"?: number; // converted from micros\n \"metrics.ctr\"?: number;\n \"metrics.average_cpc\"?: number; // converted from micros\n \"metrics.average_cpm\"?: number; // converted from micros\n \"metrics.conversions\"?: number;\n \"metrics.conversions_value\"?: number;\n \"metrics.cost_per_conversion\"?: number; // converted from micros\n \"metrics.conversions_from_interactions_rate\"?: number;\n \"metrics.all_conversions\"?: number;\n \"metrics.all_conversions_value\"?: number;\n \"metrics.cost_per_all_conversions\"?: number;\n \"metrics.view_through_conversions\"?: number;\n \"metrics.cross_device_conversions\"?: number;\n \"metrics.video_trueview_views\"?: number;\n \"metrics.video_trueview_view_rate\"?: number;\n \"metrics.video_quartile_p25_rate\"?: number;\n \"metrics.video_quartile_p50_rate\"?: number;\n \"metrics.video_quartile_p75_rate\"?: number;\n \"metrics.video_quartile_p100_rate\"?: number;\n \"metrics.interactions\"?: number;\n \"metrics.interaction_rate\"?: number;\n \"metrics.engagement_rate\"?: number;\n \"metrics.search_impression_share\"?: number;\n \"metrics.search_budget_lost_impression_share\"?: number;\n \"metrics.search_rank_lost_impression_share\"?: number;\n \"metrics.search_top_impression_share\"?: number;\n \"metrics.search_absolute_top_impression_share\"?: number;\n \"metrics.top_impression_percentage\"?: number;\n \"metrics.absolute_top_impression_percentage\"?: number;\n \"metrics.invalid_clicks\"?: number;\n \"metrics.invalid_click_rate\"?: number;\n \"metrics.active_view_cpm\"?: number;\n \"metrics.active_view_ctr\"?: number;\n \"metrics.active_view_impressions\"?: number;\n \"metrics.active_view_viewability\"?: number;\n\n // Segments\n \"segments.date\"?: string;\n \"segments.day_of_week\"?: string;\n \"segments.hour\"?: number;\n \"segments.month\"?: string;\n \"segments.week\"?: string;\n \"segments.quarter\"?: string;\n \"segments.year\"?: number;\n \"segments.device\"?: string;\n \"segments.ad_network_type\"?: string;\n \"segments.slot\"?: string;\n \"segments.conversion_action\"?: string;\n \"segments.conversion_action_name\"?: string;\n \"segments.conversion_action_category\"?: string;\n \"segments.external_conversion_source\"?: string;\n \"segments.age_range\"?: string;\n \"segments.gender\"?: string;\n \"segments.geo_target_country\"?: string;\n \"segments.geo_target_region\"?: string;\n \"segments.geo_target_metro\"?: string;\n \"segments.advertising_channel_type\"?: string;\n \"segments.advertising_channel_sub_type\"?: string;\n\n // Allow additional fields\n [key: string]: unknown;\n}\n\n// ============================================\n// CALCULATED METRICS TYPES\n// ============================================\n\nexport interface GoogleAdsDerivedMetrics {\n roas?: number | null;\n valuePerConversion?: number | null;\n allConversionsRoas?: number | null;\n allConversionsValuePerConversion?: number | null;\n costPerClickDollars?: number | null;\n costPerMilleDollars?: number | null;\n conversionRate?: number | null;\n hookRate?: number | null;\n holdRate?: number | null;\n completionRate?: number | null;\n impressionShareLostTotal?: number | null;\n spendShare?: number | null;\n}\n\n// ============================================\n// QUERY TYPES\n// ============================================\n\nexport interface GoogleAdsQueryRequest {\n customerId: string;\n resource: GoogleAdsResourceType;\n metrics: string[];\n dimensions: string[];\n filters: GoogleAdsFilter[];\n startDate?: string;\n endDate?: string;\n datePreset?: GoogleAdsDatePreset;\n orderBy?: string;\n orderDirection?: \"ASC\" | \"DESC\";\n limit?: number;\n loginCustomerId?: string;\n}\n\nexport interface GoogleAdsFilter {\n field: string;\n operator: GoogleAdsFilterOperator;\n value: string | string[] | number | number[];\n}\n\n// ============================================\n// QUERY PLAN TYPES\n// ============================================\n\nexport interface GoogleAdsQueryPlan {\n queries: GoogleAdsGaqlQuery[];\n mergeStrategy: \"join\" | \"union\" | \"none\";\n joinKeys: string[];\n warnings: string[];\n errors: string[];\n estimatedApiCalls: number;\n calculatedMetrics: string[];\n}\n\nexport interface GoogleAdsGaqlQuery {\n gaql: string;\n resource: GoogleAdsResourceType;\n selectFields: string[];\n metrics: string[];\n dimensions: string[];\n description: string;\n}\n\nexport interface GoogleAdsQueryResult {\n rows: GoogleAdsInsightRow[];\n debug: GoogleAdsQueryDebugInfo;\n warnings?: string[];\n error?: string;\n}\n\nexport interface GoogleAdsQueryDebugInfo {\n requestCount: number;\n totalRows: number;\n executionTimeMs: number;\n errors: string[];\n warnings: string[];\n rawRequests: unknown[];\n rawResponses: unknown[];\n calculatedMetrics: string[];\n joinKeys: string[];\n gaqlQueries: string[];\n}\n\n// ============================================\n// METRIC CATALOG TYPES\n// ============================================\n\nexport interface GoogleAdsMetricDefinition {\n key: string;\n name: string;\n description: string;\n category: GoogleAdsMetricCategory;\n format: GoogleAdsMetricFormat;\n apiField: string;\n type: \"api\" | \"calculated\";\n formula?: string;\n dependencies?: string[];\n compatibleResources?: GoogleAdsResourceType[];\n incompatibleSegments?: string[];\n}\n\n// ============================================\n// DIMENSION CATALOG TYPES\n// ============================================\n\nexport interface GoogleAdsDimensionDefinition {\n key: string;\n name: string;\n description: string;\n category: GoogleAdsDimensionCategory;\n apiField: string;\n isSegment: boolean;\n isResourceAttribute: boolean;\n compatibleResources?: GoogleAdsResourceType[];\n incompatibleWith?: string[];\n possibleValues?: string[];\n requiresSegment?: string;\n}\n\n// ============================================\n// FILTER CATALOG TYPES\n// ============================================\n\nexport interface GoogleAdsFilterDefinition {\n key: string;\n name: string;\n description: string;\n apiField: string;\n operators: GoogleAdsFilterOperator[];\n type: \"enum\" | \"string\" | \"number\" | \"boolean\" | \"date\";\n enumValues?: string[];\n defaultOperator?: GoogleAdsFilterOperator;\n defaultValue?: string;\n}\n\n// ============================================\n// KEYWORD PLANNER (PLANLESS, READ-ONLY RPCS)\n// ============================================\n\nexport type KeywordPlanNetwork = \"GOOGLE_SEARCH\" | \"GOOGLE_SEARCH_AND_PARTNERS\";\nexport type KeywordMatchType = \"EXACT\" | \"PHRASE\" | \"BROAD\";\nexport type MonthOfYear =\n | \"JANUARY\"\n | \"FEBRUARY\"\n | \"MARCH\"\n | \"APRIL\"\n | \"MAY\"\n | \"JUNE\"\n | \"JULY\"\n | \"AUGUST\"\n | \"SEPTEMBER\"\n | \"OCTOBER\"\n | \"NOVEMBER\"\n | \"DECEMBER\";\n\nexport interface KeywordPlanYearMonth {\n year: string;\n month: MonthOfYear;\n}\n\nexport interface KeywordPlanHistoricalMetricsOptions {\n yearMonthRange?: {\n start: KeywordPlanYearMonth;\n end: KeywordPlanYearMonth;\n };\n includeAverageCpc?: boolean;\n}\n\nexport interface KeywordPlanAggregateMetrics {\n aggregateMetricTypes: Array<\"DEVICE\">;\n}\n\nexport interface GenerateKeywordHistoricalMetricsRequest {\n keywords: string[];\n geoTargetConstants?: string[];\n language?: string;\n keywordPlanNetwork?: KeywordPlanNetwork;\n includeAdultKeywords?: boolean;\n historicalMetricsOptions?: KeywordPlanHistoricalMetricsOptions;\n aggregateMetrics?: KeywordPlanAggregateMetrics;\n}\n\nexport interface KeywordPlanMonthlySearchVolume {\n year?: string;\n month?: MonthOfYear | \"UNSPECIFIED\" | \"UNKNOWN\";\n monthlySearches?: string | null;\n}\n\nexport interface KeywordPlanHistoricalMetrics {\n avgMonthlySearches?: string;\n competition?: \"UNSPECIFIED\" | \"UNKNOWN\" | \"LOW\" | \"MEDIUM\" | \"HIGH\";\n competitionIndex?: string;\n lowTopOfPageBidMicros?: string;\n highTopOfPageBidMicros?: string;\n averageCpcMicros?: string;\n monthlySearchVolumes?: KeywordPlanMonthlySearchVolume[];\n}\n\nexport interface KeywordPlanDeviceSearches {\n device?: \"UNSPECIFIED\" | \"UNKNOWN\" | \"MOBILE\" | \"TABLET\" | \"DESKTOP\" | \"CONNECTED_TV\" | \"OTHER\";\n searchCount?: string;\n}\n\nexport interface KeywordPlanAggregateMetricResults {\n deviceSearches?: KeywordPlanDeviceSearches[];\n}\n\nexport interface GenerateKeywordHistoricalMetricsResult {\n text?: string;\n closeVariants?: string[];\n keywordMetrics?: KeywordPlanHistoricalMetrics;\n}\n\nexport interface GenerateKeywordHistoricalMetricsResponse {\n results?: GenerateKeywordHistoricalMetricsResult[];\n aggregateMetricResults?: KeywordPlanAggregateMetricResults;\n}\n\nexport interface KeywordConceptGroup {\n name?: string;\n type?: \"UNSPECIFIED\" | \"UNKNOWN\" | \"BRAND\" | \"OTHER_BRANDS\" | \"NON_BRAND\";\n}\n\nexport interface KeywordConcept {\n name?: string;\n conceptGroup?: KeywordConceptGroup;\n}\n\nexport interface KeywordAnnotations {\n concepts?: KeywordConcept[];\n}\n\nexport interface GenerateKeywordIdeasRequest {\n geoTargetConstants?: string[];\n language?: string;\n keywordPlanNetwork?: KeywordPlanNetwork;\n includeAdultKeywords?: boolean;\n historicalMetricsOptions?: KeywordPlanHistoricalMetricsOptions;\n aggregateMetrics?: KeywordPlanAggregateMetrics;\n keywordAnnotation?: Array<\"KEYWORD_CONCEPT\">;\n pageSize?: number;\n pageToken?: string;\n keywordSeed?: { keywords: string[] };\n urlSeed?: { url: string };\n keywordAndUrlSeed?: { keywords: string[]; url: string };\n siteSeed?: { site: string };\n}\n\nexport interface GenerateKeywordIdeaResult {\n text?: string;\n closeVariants?: string[];\n keywordIdeaMetrics?: KeywordPlanHistoricalMetrics;\n keywordAnnotations?: KeywordAnnotations;\n}\n\nexport interface GenerateKeywordIdeasResponse {\n results?: GenerateKeywordIdeaResult[];\n nextPageToken?: string;\n totalSize?: string;\n aggregateMetricResults?: KeywordPlanAggregateMetricResults;\n}\n\nexport interface KeywordInfo {\n text: string;\n matchType: KeywordMatchType;\n}\n\nexport interface KeywordForecastMetrics {\n impressions?: number;\n clicks?: number;\n costMicros?: string;\n clickThroughRate?: number;\n averageCpcMicros?: string;\n conversions?: number;\n conversionRate?: number;\n averageCpaMicros?: string;\n}\n\nexport interface CampaignToForecast {\n keywordPlanNetwork: KeywordPlanNetwork;\n biddingStrategy: {\n manualCpcBiddingStrategy?: { maxCpcBidMicros: string; dailyBudgetMicros?: string };\n maximizeClicksBiddingStrategy?: { dailyTargetSpendMicros: string; maxCpcBidCeilingMicros?: string };\n maximizeConversionsBiddingStrategy?: { dailyTargetSpendMicros: string };\n };\n adGroups: Array<{\n biddableKeywords: Array<{ keyword: KeywordInfo; maxCpcBidMicros?: string }>;\n negativeKeywords?: KeywordInfo[];\n maxCpcBidMicros?: string;\n }>;\n geoModifiers?: Array<{ geoTargetConstant: string; bidModifier?: number }>;\n languageConstants?: string[];\n negativeKeywords?: KeywordInfo[];\n conversionRate?: number;\n}\n\nexport interface GenerateKeywordForecastMetricsRequest {\n campaign: CampaignToForecast;\n currencyCode?: string;\n forecastPeriod?: { startDate: string; endDate: string };\n}\n\nexport interface GenerateKeywordForecastMetricsResponse {\n campaignForecastMetrics?: KeywordForecastMetrics;\n}\n\nexport type GoogleAdsFieldCategory =\n | \"UNSPECIFIED\"\n | \"UNKNOWN\"\n | \"RESOURCE\"\n | \"ATTRIBUTE\"\n | \"SEGMENT\"\n | \"METRIC\";\n\nexport interface GoogleAdsField {\n resourceName?: string;\n name?: string;\n category?: GoogleAdsFieldCategory;\n dataType?: string;\n typeUrl?: string;\n selectable?: boolean;\n filterable?: boolean;\n sortable?: boolean;\n isRepeated?: boolean;\n enumValues?: string[];\n selectableWith?: string[];\n attributeResources?: string[];\n metrics?: string[];\n segments?: string[];\n}\n\nexport interface SearchGoogleAdsFieldsRequest {\n query: string;\n pageSize?: number;\n pageToken?: string;\n}\n\nexport interface SearchGoogleAdsFieldsResponse {\n results?: GoogleAdsField[];\n nextPageToken?: string;\n totalResultsCount?: string;\n}\n\nexport interface GeoTargetConstant {\n resourceName?: string;\n id?: string;\n name?: string;\n canonicalName?: string;\n parentGeoTarget?: string;\n countryCode?: string;\n targetType?: string;\n status?: \"UNSPECIFIED\" | \"UNKNOWN\" | \"ENABLED\" | \"REMOVAL_PLANNED\";\n}\n\nexport interface SuggestGeoTargetConstantsRequest {\n locale?: string;\n countryCode?: string;\n locationNames?: { names: string[] };\n geoTargets?: { geoTargetConstants: string[] };\n}\n\nexport interface GeoTargetConstantSuggestion {\n searchTerm?: string;\n locale?: string;\n reach?: string;\n geoTargetConstant?: GeoTargetConstant;\n geoTargetConstantParents?: GeoTargetConstant[];\n}\n\nexport interface SuggestGeoTargetConstantsResponse {\n geoTargetConstantSuggestions?: GeoTargetConstantSuggestion[];\n}\n\nexport interface GenerateAdGroupThemesRequest {\n keywords: string[];\n adGroups: string[];\n}\n\nexport interface AdGroupKeywordSuggestion {\n keywordText?: string;\n suggestedKeywordText?: string;\n suggestedMatchType?: \"UNSPECIFIED\" | \"UNKNOWN\" | \"EXACT\" | \"PHRASE\" | \"BROAD\";\n suggestedAdGroup?: string;\n suggestedCampaign?: string;\n}\n\nexport interface UnusableAdGroup {\n adGroup?: string;\n campaign?: string;\n}\n\nexport interface GenerateAdGroupThemesResponse {\n adGroupKeywordSuggestions?: AdGroupKeywordSuggestion[];\n unusableAdGroups?: UnusableAdGroup[];\n}\n\n// ============================================\n// API ERROR TYPES\n// ============================================\n\nexport interface GoogleAdsApiError {\n code: number;\n message: string;\n status: string;\n details?: Array<{\n \"@type\": string;\n errors?: Array<{\n errorCode?: Record<string, string>;\n message?: string;\n trigger?: { stringValue?: string };\n location?: { fieldPathElements?: Array<{ fieldName?: string; index?: number }> };\n }>;\n requestId?: string;\n }>;\n}\n\nexport class GoogleAdsApiException extends Error {\n code: number;\n status: string;\n requestId?: string;\n errors: Array<{\n errorCode?: Record<string, string>;\n message?: string;\n }>;\n\n constructor(\n message: string,\n code: number,\n status?: string,\n requestId?: string,\n errors?: Array<{ errorCode?: Record<string, string>; message?: string }>\n ) {\n super(message);\n this.name = \"GoogleAdsApiException\";\n this.code = code;\n this.status = status || \"UNKNOWN\";\n this.requestId = requestId;\n this.errors = errors || [];\n }\n\n get isAuthError(): boolean {\n return this.code === 401 || this.code === 403;\n }\n\n get isRateLimitError(): boolean {\n return this.code === 429;\n }\n\n get isQuotaError(): boolean {\n return this.code === 429 || this.status === \"RESOURCE_EXHAUSTED\";\n }\n\n get isInvalidQueryError(): boolean {\n return this.code === 400;\n }\n\n get suggestion(): string {\n if (this.isAuthError) return \"Re-authenticate with Google Ads\";\n if (this.isRateLimitError) return \"Wait and retry -- rate limit exceeded\";\n if (this.isQuotaError) return \"Quota exceeded -- reduce query frequency\";\n if (this.isInvalidQueryError) return \"Check request fields, criterion IDs, date ranges, and GAQL syntax/field compatibility when applicable\";\n return \"Check the error details for more information\";\n }\n}\n\n// ============================================\n// CONSTANTS\n// ============================================\n\nexport const GOOGLE_ADS_API_VERSION = \"v25\";\nexport const GOOGLE_ADS_API_BASE_URL = `https://googleads.googleapis.com/${GOOGLE_ADS_API_VERSION}`;\n\nexport const MICRO_CURRENCY_FACTOR = 1_000_000;\n\n/**\n * Fields that contain micro-currency values and need conversion\n */\nexport const MICRO_CURRENCY_FIELDS = [\n \"metrics.cost_micros\",\n \"metrics.average_cpc\",\n \"metrics.average_cpm\",\n \"metrics.cost_per_conversion\",\n \"metrics.cost_per_all_conversions\",\n \"metrics.average_cost\",\n \"metrics.average_cpe\",\n \"metrics.average_cpv\",\n \"metrics.trueview_average_cpv\",\n \"metrics.active_view_cpm\",\n \"metrics.active_view_measurable_cost_micros\",\n \"campaign_budget.amount_micros\",\n \"campaign_budget.total_amount_micros\",\n] as const;\n\n/**\n * camelCase to snake_case mapping for API response fields\n */\nexport const CAMEL_TO_SNAKE_METRIC_MAP: Record<string, string> = {\n impressions: \"impressions\",\n clicks: \"clicks\",\n costMicros: \"cost_micros\",\n ctr: \"ctr\",\n averageCpc: \"average_cpc\",\n averageCpm: \"average_cpm\",\n conversions: \"conversions\",\n conversionsValue: \"conversions_value\",\n costPerConversion: \"cost_per_conversion\",\n conversionsFromInteractionsRate: \"conversions_from_interactions_rate\",\n allConversions: \"all_conversions\",\n allConversionsValue: \"all_conversions_value\",\n costPerAllConversions: \"cost_per_all_conversions\",\n viewThroughConversions: \"view_through_conversions\",\n crossDeviceConversions: \"cross_device_conversions\",\n videoViews: \"video_views\",\n videoViewRate: \"video_view_rate\",\n videoQuartileP25Rate: \"video_quartile_p25_rate\",\n videoQuartileP50Rate: \"video_quartile_p50_rate\",\n videoQuartileP75Rate: \"video_quartile_p75_rate\",\n videoQuartileP100Rate: \"video_quartile_p100_rate\",\n interactions: \"interactions\",\n interactionRate: \"interaction_rate\",\n engagementRate: \"engagement_rate\",\n searchImpressionShare: \"search_impression_share\",\n searchBudgetLostImpressionShare: \"search_budget_lost_impression_share\",\n searchRankLostImpressionShare: \"search_rank_lost_impression_share\",\n searchTopImpressionShare: \"search_top_impression_share\",\n searchAbsoluteTopImpressionShare: \"search_absolute_top_impression_share\",\n searchExactMatchImpressionShare: \"search_exact_match_impression_share\",\n contentImpressionShare: \"content_impression_share\",\n contentBudgetLostImpressionShare: \"content_budget_lost_impression_share\",\n contentRankLostImpressionShare: \"content_rank_lost_impression_share\",\n topImpressionPercentage: \"top_impression_percentage\",\n absoluteTopImpressionPercentage: \"absolute_top_impression_percentage\",\n invalidClicks: \"invalid_clicks\",\n invalidClickRate: \"invalid_click_rate\",\n activeViewCpm: \"active_view_cpm\",\n activeViewCtr: \"active_view_ctr\",\n activeViewImpressions: \"active_view_impressions\",\n activeViewMeasurability: \"active_view_measurability\",\n activeViewMeasurableCostMicros: \"active_view_measurable_cost_micros\",\n activeViewMeasurableImpressions: \"active_view_measurable_impressions\",\n activeViewViewability: \"active_view_viewability\",\n reach: \"reach\",\n frequencyCount: \"frequency_count\",\n averageCost: \"average_cost\",\n averageCpe: \"average_cpe\",\n averageCpv: \"average_cpv\",\n};\n\nexport const CAMEL_TO_SNAKE_SEGMENT_MAP: Record<string, string> = {\n date: \"date\",\n dayOfWeek: \"day_of_week\",\n hour: \"hour\",\n month: \"month\",\n week: \"week\",\n quarter: \"quarter\",\n year: \"year\",\n device: \"device\",\n adNetworkType: \"ad_network_type\",\n slot: \"slot\",\n conversionAction: \"conversion_action\",\n conversionActionName: \"conversion_action_name\",\n conversionActionCategory: \"conversion_action_category\",\n externalConversionSource: \"external_conversion_source\",\n clickType: \"click_type\",\n adDestinationType: \"ad_destination_type\",\n ageRange: \"age_range\",\n gender: \"gender\",\n geoTargetCountry: \"geo_target_country\",\n geoTargetRegion: \"geo_target_region\",\n geoTargetMetro: \"geo_target_metro\",\n geoTargetCity: \"geo_target_city\",\n advertisingChannelType: \"advertising_channel_type\",\n advertisingChannelSubType: \"advertising_channel_sub_type\",\n productItemId: \"product_item_id\",\n productBrand: \"product_brand\",\n productCategoryLevel1: \"product_category_level1\",\n productCategoryLevel2: \"product_category_level2\",\n productTypeL1: \"product_type_l1\",\n productCustomAttribute0: \"product_custom_attribute0\",\n};\n\n// ============================================\n// UTILITY FUNCTIONS\n// ============================================\n\n/**\n * Convert micro-currency to standard currency\n */\nexport function microToStandard(microAmount: number | string): number {\n const amount = typeof microAmount === \"string\" ? parseFloat(microAmount) : microAmount;\n return amount / MICRO_CURRENCY_FACTOR;\n}\n\n/**\n * Format a Google Ads customer ID (add dashes: 1234567890 -> 123-456-7890)\n */\nexport function formatCustomerId(customerId: string): string {\n const clean = customerId.replace(/-/g, \"\");\n if (clean.length !== 10) return customerId;\n return `${clean.slice(0, 3)}-${clean.slice(3, 6)}-${clean.slice(6)}`;\n}\n\n/**\n * Strip dashes from customer ID for API calls\n */\nexport function stripCustomerId(customerId: string): string {\n return customerId.replace(/-/g, \"\");\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ADS API v23 METRIC CATALOG\n// Complete catalog of 120+ Google Ads metrics\n// Based on Google Ads API v23 GAQL metric fields\n// ============================================\n\nimport {\n GoogleAdsMetricDefinition,\n GoogleAdsMetricCategory,\n GoogleAdsResourceType,\n} from \"./types.js\";\n\n// ============================================\n// CORE PERFORMANCE METRICS\n// ============================================\n\nconst CORE_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"impressions\",\n name: \"Impressions\",\n description: \"Total number of times your ads were shown on a search results page or website.\",\n category: \"core\",\n format: \"number\",\n apiField: \"metrics.impressions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"clicks\",\n name: \"Clicks\",\n description: \"Total number of clicks on your ads.\",\n category: \"core\",\n format: \"number\",\n apiField: \"metrics.clicks\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"ctr\",\n name: \"CTR\",\n description: \"Click-through rate, the ratio of clicks to impressions.\",\n category: \"core\",\n format: \"percentage\",\n apiField: \"metrics.ctr\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"interactions\",\n name: \"Interactions\",\n description: \"Number of interactions, including clicks, swipes, and video views depending on ad type.\",\n category: \"core\",\n format: \"number\",\n apiField: \"metrics.interactions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"interactionRate\",\n name: \"Interaction Rate\",\n description: \"The ratio of interactions to impressions for your ad.\",\n category: \"core\",\n format: \"percentage\",\n apiField: \"metrics.interaction_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"engagementRate\",\n name: \"Engagement Rate\",\n description: \"The rate of engagements (expansions of lightbox ads) divided by impressions.\",\n category: \"core\",\n format: \"percentage\",\n apiField: \"metrics.engagement_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"reach\",\n name: \"Reach\",\n description: \"Estimated number of unique users who saw your ad at least once.\",\n category: \"core\",\n format: \"number\",\n apiField: \"metrics.reach\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"ad_group_ad\", \"customer\"],\n },\n {\n key: \"frequency\",\n name: \"Frequency\",\n description: \"Average number of times a unique user saw your ad.\",\n category: \"core\",\n format: \"ratio\",\n apiField: \"metrics.frequency\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"ad_group_ad\", \"customer\"],\n },\n {\n key: \"absoluteTopImpressionPercentage\",\n name: \"Absolute Top Impression %\",\n description: \"Percentage of ad impressions shown as the very first ad above organic results.\",\n category: \"core\",\n format: \"percentage\",\n apiField: \"metrics.absolute_top_impression_percentage\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"ad_group_criterion\",\n \"customer\",\n ],\n },\n {\n key: \"topImpressionPercentage\",\n name: \"Top Impression %\",\n description: \"Percentage of ad impressions shown anywhere above organic search results.\",\n category: \"core\",\n format: \"percentage\",\n apiField: \"metrics.top_impression_percentage\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"ad_group_criterion\",\n \"customer\",\n ],\n },\n {\n key: \"interactionEventTypes\",\n name: \"Interaction Event Types\",\n description: \"The types of interactions that are counted as interactions for this campaign type.\",\n category: \"core\",\n format: \"string\",\n apiField: \"metrics.interaction_event_types\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// SPEND / COST METRICS\n// ============================================\n\nconst SPEND_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"costMicros\",\n name: \"Cost\",\n description: \"Total cost of all clicks and interactions in micro-currency units.\",\n category: \"spend\",\n format: \"micro_currency\",\n apiField: \"metrics.cost_micros\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"averageCpc\",\n name: \"Avg. CPC\",\n description: \"Average cost-per-click, the total cost divided by total clicks.\",\n category: \"spend\",\n format: \"micro_currency\",\n apiField: \"metrics.average_cpc\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"averageCpm\",\n name: \"Avg. CPM\",\n description: \"Average cost per one thousand impressions.\",\n category: \"spend\",\n format: \"micro_currency\",\n apiField: \"metrics.average_cpm\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"averageCost\",\n name: \"Avg. Cost\",\n description: \"Average amount paid per interaction.\",\n category: \"spend\",\n format: \"micro_currency\",\n apiField: \"metrics.average_cost\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"averageCpe\",\n name: \"Avg. CPE\",\n description: \"Average cost per engagement (lightbox ad interactions).\",\n category: \"spend\",\n format: \"micro_currency\",\n apiField: \"metrics.average_cpe\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"averageCpv\",\n name: \"Avg. CPV\",\n description: \"Average cost per video view.\",\n category: \"spend\",\n format: \"micro_currency\",\n apiField: \"metrics.trueview_average_cpv\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"costPerCurrentModelAttributedConversion\",\n name: \"Cost / Current Model Conversion\",\n description: \"Cost per conversion using the current attribution model.\",\n category: \"spend\",\n format: \"micro_currency\",\n apiField: \"metrics.cost_per_current_model_attributed_conversion\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// CONVERSION METRICS\n// ============================================\n\nconst CONVERSION_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"conversions\",\n name: \"Conversions\",\n description: \"Total number of conversions from conversion actions included in the Conversions column.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.conversions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"conversionsValue\",\n name: \"Conversions Value\",\n description: \"Total value of conversions included in the Conversions column.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.conversions_value\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"costPerConversion\",\n name: \"Cost / Conversion\",\n description: \"Average cost per conversion.\",\n category: \"conversion\",\n format: \"micro_currency\",\n apiField: \"metrics.cost_per_conversion\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"conversionsFromInteractionsRate\",\n name: \"Conversion Rate (Interaction)\",\n description: \"Conversions from interactions divided by the number of ad interactions.\",\n category: \"conversion\",\n format: \"percentage\",\n apiField: \"metrics.conversions_from_interactions_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"conversionsValuePerCost\",\n name: \"Conversions Value / Cost\",\n description: \"The value of conversions divided by the cost of ad interactions.\",\n category: \"conversion\",\n format: \"ratio\",\n apiField: \"metrics.conversions_value_per_cost\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"allConversions\",\n name: \"All Conversions\",\n description: \"Total number of all conversions, including those not included in the Conversions column.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.all_conversions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsValue\",\n name: \"All Conversions Value\",\n description: \"Total value of all conversions, including those not in the Conversions column.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.all_conversions_value\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"costPerAllConversions\",\n name: \"Cost / All Conversions\",\n description: \"Average cost per all conversions.\",\n category: \"conversion\",\n format: \"micro_currency\",\n apiField: \"metrics.cost_per_all_conversions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsFromInteractionsRate\",\n name: \"All Conversions Rate (Interaction)\",\n description: \"All conversions from interactions divided by the number of ad interactions.\",\n category: \"conversion\",\n format: \"percentage\",\n apiField: \"metrics.all_conversions_from_interactions_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsValuePerCost\",\n name: \"All Conversions Value / Cost\",\n description: \"The value of all conversions divided by the cost of ad interactions.\",\n category: \"conversion\",\n format: \"ratio\",\n apiField: \"metrics.all_conversions_value_per_cost\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"viewThroughConversions\",\n name: \"View-Through Conversions\",\n description: \"Conversions from when a user saw but did not click your ad.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.view_through_conversions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"crossDeviceConversions\",\n name: \"Cross-Device Conversions\",\n description: \"Conversions where the user interacted on one device and converted on another.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.cross_device_conversions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"currentModelAttributedConversions\",\n name: \"Current Model Conversions\",\n description: \"Conversions attributed using the current attribution model for the conversion action.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.current_model_attributed_conversions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"customer\",\n ],\n },\n {\n key: \"currentModelAttributedConversionsValue\",\n name: \"Current Model Conversions Value\",\n description: \"Value of conversions attributed using the current attribution model.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.current_model_attributed_conversions_value\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"customer\",\n ],\n },\n {\n key: \"conversionsFromInteractionsValuePerInteraction\",\n name: \"Conv. Value / Interaction\",\n description: \"Value of conversions from interactions divided by total interactions.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.conversions_from_interactions_value_per_interaction\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"customer\",\n ],\n },\n {\n key: \"valuePerAllConversions\",\n name: \"Value / All Conversions\",\n description: \"The value of all conversions divided by the number of all conversions.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.value_per_all_conversions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"valuePerConversionApi\",\n name: \"Value / Conversion\",\n description: \"The value of conversions divided by the number of conversions.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.value_per_conversion\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"valuePerCurrentModelAttributedConversion\",\n name: \"Value / Current Model Conversion\",\n description: \"Value of current model attributed conversions divided by current model conversions.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.value_per_current_model_attributed_conversion\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsFromClickToCall\",\n name: \"All Conv. from Click to Call\",\n description: \"Number of phone call conversions from click-to-call ads.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.all_conversions_from_click_to_call\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsFromDirections\",\n name: \"All Conv. from Directions\",\n description: \"Number of direction-getting conversions from local ads.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.all_conversions_from_directions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsFromMenu\",\n name: \"All Conv. from Menu\",\n description: \"Number of menu-viewing conversions from local ads.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.all_conversions_from_menu\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsFromOrder\",\n name: \"All Conv. from Order\",\n description: \"Number of order conversions from local ads.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.all_conversions_from_order\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsFromStoreVisit\",\n name: \"All Conv. from Store Visit\",\n description: \"Number of estimated store visit conversions.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.all_conversions_from_store_visit\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsFromStoreWebsite\",\n name: \"All Conv. from Store Website\",\n description: \"Number of store website conversions from local ads.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.all_conversions_from_store_website\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n // --- Conversion Date Attribution Metrics ---\n // These metrics attribute conversions to the DATE THE CONVERSION OCCURRED,\n // NOT the date of the click/impression. Standard metrics (conversions, conversions_value)\n // attribute to click date. These are useful for finance/analytics reporting.\n {\n key: \"conversionsByConversionDate\",\n name: \"Conversions (Conv. Time)\",\n description: \"Conversions attributed to the date the conversion occurred, not the click date. Useful for understanding actual conversion timing and reporting lag.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.conversions_by_conversion_date\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"conversionsValueByConversionDate\",\n name: \"Conversions Value (Conv. Time)\",\n description: \"Revenue attributed to the date the conversion occurred, not the click date. Note: cannot be divided by cost for ROAS since cost is attributed to spend date.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.conversions_value_by_conversion_date\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsByConversionDate\",\n name: \"All Conversions (Conv. Time)\",\n description: \"All conversions (including secondary) attributed to the date the conversion occurred, not the click date.\",\n category: \"conversion\",\n format: \"number\",\n apiField: \"metrics.all_conversions_by_conversion_date\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsValueByConversionDate\",\n name: \"All Conversions Value (Conv. Time)\",\n description: \"Total value of all conversions (including secondary) attributed to the date the conversion occurred, not the click date.\",\n category: \"conversion\",\n format: \"currency\",\n apiField: \"metrics.all_conversions_value_by_conversion_date\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// IMPRESSION SHARE METRICS\n// ============================================\n\nconst IMPRESSION_SHARE_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"searchImpressionShare\",\n name: \"Search Impression Share\",\n description: \"Percentage of eligible search impressions your ads received.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchBudgetLostImpressionShare\",\n name: \"Search Lost IS (Budget)\",\n description: \"Estimated percentage of search impressions lost due to insufficient budget.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_budget_lost_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchRankLostImpressionShare\",\n name: \"Search Lost IS (Rank)\",\n description: \"Estimated percentage of search impressions lost due to poor Ad Rank.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_rank_lost_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchTopImpressionShare\",\n name: \"Search Top IS\",\n description: \"Percentage of eligible top-of-page search impressions your ads received.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_top_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchAbsoluteTopImpressionShare\",\n name: \"Search Abs. Top IS\",\n description: \"Percentage of eligible absolute top search impressions your ads received.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_absolute_top_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchExactMatchImpressionShare\",\n name: \"Search Exact Match IS\",\n description: \"Percentage of eligible impressions received for searches that exactly matched your keywords.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_exact_match_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchBudgetLostAbsoluteTopImpressionShare\",\n name: \"Search Lost Abs. Top IS (Budget)\",\n description: \"Estimated percentage of absolute top search impressions lost due to budget.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_budget_lost_absolute_top_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchBudgetLostTopImpressionShare\",\n name: \"Search Lost Top IS (Budget)\",\n description: \"Estimated percentage of top search impressions lost due to budget.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_budget_lost_top_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchRankLostAbsoluteTopImpressionShare\",\n name: \"Search Lost Abs. Top IS (Rank)\",\n description: \"Estimated percentage of absolute top search impressions lost due to Ad Rank.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_rank_lost_absolute_top_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"searchRankLostTopImpressionShare\",\n name: \"Search Lost Top IS (Rank)\",\n description: \"Estimated percentage of top search impressions lost due to Ad Rank.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.search_rank_lost_top_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"contentImpressionShare\",\n name: \"Content Impression Share\",\n description: \"Percentage of eligible Display Network impressions your ads received.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.content_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"contentBudgetLostImpressionShare\",\n name: \"Content Lost IS (Budget)\",\n description: \"Estimated percentage of Display Network impressions lost due to budget.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.content_budget_lost_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n {\n key: \"contentRankLostImpressionShare\",\n name: \"Content Lost IS (Rank)\",\n description: \"Estimated percentage of Display Network impressions lost due to Ad Rank.\",\n category: \"impression_share\",\n format: \"percentage\",\n apiField: \"metrics.content_rank_lost_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\"],\n incompatibleSegments: [\"segments.click_type\", \"segments.ad_destination_type\"],\n },\n];\n\n// ============================================\n// VIDEO METRICS\n// ============================================\n\nconst VIDEO_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"videoViews\",\n name: \"Video Views\",\n description: \"Total number of times your video ads were viewed.\",\n category: \"video\",\n format: \"number\",\n apiField: \"metrics.video_trueview_views\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoViewRate\",\n name: \"Video View Rate\",\n description: \"The rate of video views relative to impressions.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.video_trueview_view_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoQuartileP25Rate\",\n name: \"Video Played to 25%\",\n description: \"Percentage of impressions where the video played to 25% of its length.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.video_quartile_p25_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoQuartileP50Rate\",\n name: \"Video Played to 50%\",\n description: \"Percentage of impressions where the video played to 50% of its length.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.video_quartile_p50_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoQuartileP75Rate\",\n name: \"Video Played to 75%\",\n description: \"Percentage of impressions where the video played to 75% of its length.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.video_quartile_p75_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoQuartileP100Rate\",\n name: \"Video Played to 100%\",\n description: \"Percentage of impressions where the video played to 100% of its length.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.video_quartile_p100_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"engagements\",\n name: \"Engagements\",\n description: \"Number of engagements, such as clicks on lightbox ads or video interactions.\",\n category: \"video\",\n format: \"number\",\n apiField: \"metrics.engagements\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"averageVideoViewDuration\",\n name: \"Avg. Video View Duration\",\n description: \"Average duration in seconds of all video views.\",\n category: \"video\",\n format: \"duration\",\n apiField: \"metrics.average_video_watch_time_duration_millis\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoDuration\",\n name: \"Video Duration\",\n description: \"Average duration in milliseconds of the video ad.\",\n category: \"video\",\n format: \"duration\",\n apiField: \"metrics.video_duration_millis\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"video\",\n ],\n },\n {\n key: \"videoViewsP25\",\n name: \"Video Views (25%)\",\n description: \"Number of times your video played to 25% of its length.\",\n category: \"video\",\n format: \"number\",\n apiField: \"metrics.video_quartile_p25\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoViewsP50\",\n name: \"Video Views (50%)\",\n description: \"Number of times your video played to 50% of its length.\",\n category: \"video\",\n format: \"number\",\n apiField: \"metrics.video_quartile_p50\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoViewsP75\",\n name: \"Video Views (75%)\",\n description: \"Number of times your video played to 75% of its length.\",\n category: \"video\",\n format: \"number\",\n apiField: \"metrics.video_quartile_p75\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"videoViewsP100\",\n name: \"Video Views (100%)\",\n description: \"Number of times your video played to 100% of its length.\",\n category: \"video\",\n format: \"number\",\n apiField: \"metrics.video_quartile_p100\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"averageTimeOnSite\",\n name: \"Avg. Time on Site\",\n description: \"Average duration of all sessions on the website from ad clicks, in seconds.\",\n category: \"video\",\n format: \"duration\",\n apiField: \"metrics.average_time_on_site\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"customer\",\n ],\n },\n {\n key: \"bounceRate\",\n name: \"Bounce Rate\",\n description: \"Percentage of clicks where the user only visited a single page on your site.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.bounce_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"customer\",\n ],\n },\n {\n key: \"percentNewVisitors\",\n name: \"% New Visitors\",\n description: \"Percentage of first-time sessions from ad clicks.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.percent_new_visitors\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"customer\",\n ],\n },\n {\n key: \"trueViews\",\n name: \"TrueViews\",\n description: \"Number of TrueView video ad views. A view is counted when someone watches 30 seconds of the video (or the entire ad if shorter) or interacts with the ad. Equivalent to Video Views for YouTube campaigns.\",\n category: \"video\",\n format: \"number\",\n apiField: \"metrics.video_trueview_views\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"trueViewRate\",\n name: \"TrueView Rate\",\n description: \"TrueView rate: the number of views divided by impressions. Shows how often people chose to watch your video ad. Use segments.ad_network_type to break down by In-Stream vs In-Feed.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.video_trueview_view_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"trueViewRateInStream\",\n name: \"TrueView Rate (In-Stream)\",\n description: \"TrueView view rate for In-Stream ads (skippable pre-roll, mid-roll). Add segments.ad_network_type = YOUTUBE_WATCH dimension to your query for accurate In-Stream breakdown.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.video_trueview_view_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"trueViewRateInFeed\",\n name: \"TrueView Rate (In-Feed)\",\n description: \"TrueView view rate for In-Feed/Discovery ads. Add segments.ad_network_type = YOUTUBE_SEARCH dimension to your query for accurate In-Feed breakdown.\",\n category: \"video\",\n format: \"percentage\",\n apiField: \"metrics.video_trueview_view_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"trueViewShorts\",\n name: \"TrueView (Shorts)\",\n description: \"TrueView views from YouTube Shorts placement. Add segments.ad_network_type dimension and filter for Shorts format to isolate Shorts-specific views.\",\n category: \"video\",\n format: \"number\",\n apiField: \"metrics.video_trueview_views\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"geographic_view\",\n \"user_location_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"video\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// ENGAGEMENT METRICS (Gmail & Display)\n// ============================================\n\nconst ENGAGEMENT_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"gmailForwards\",\n name: \"Gmail Forwards\",\n description: \"Number of times your Gmail ad was forwarded to someone else.\",\n category: \"engagement\",\n format: \"number\",\n apiField: \"metrics.gmail_forwards\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"gmailSaves\",\n name: \"Gmail Saves\",\n description: \"Number of times someone saved your Gmail ad to their inbox.\",\n category: \"engagement\",\n format: \"number\",\n apiField: \"metrics.gmail_saves\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"gmailSecondaryClicks\",\n name: \"Gmail Secondary Clicks\",\n description: \"Number of clicks to the landing page on the expanded state of your Gmail ad.\",\n category: \"engagement\",\n format: \"number\",\n apiField: \"metrics.gmail_secondary_clicks\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"phoneCalls\",\n name: \"Phone Calls\",\n description: \"Number of offline phone calls initiated from call extensions or call-only ads.\",\n category: \"engagement\",\n format: \"number\",\n apiField: \"metrics.phone_calls\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n {\n key: \"phoneImpressions\",\n name: \"Phone Impressions\",\n description: \"Number of times your phone number was shown with your ad.\",\n category: \"engagement\",\n format: \"number\",\n apiField: \"metrics.phone_impressions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n {\n key: \"phoneThroughRate\",\n name: \"Phone Through Rate\",\n description: \"Number of phone calls divided by number of phone impressions.\",\n category: \"engagement\",\n format: \"percentage\",\n apiField: \"metrics.phone_through_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// QUALITY SCORE METRICS\n// ============================================\n\nconst QUALITY_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"qualityScore\",\n name: \"Quality Score\",\n description: \"Current quality score of the keyword on a 1-10 scale.\",\n category: \"quality\",\n format: \"number\",\n apiField: \"ad_group_criterion.quality_info.quality_score\",\n type: \"api\",\n compatibleResources: [\"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"historicalQualityScore\",\n name: \"Historical Quality Score\",\n description: \"Historical quality score at the time of the last report snapshot.\",\n category: \"quality\",\n format: \"number\",\n apiField: \"metrics.historical_quality_score\",\n type: \"api\",\n compatibleResources: [\"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"historicalCreativeQualityScore\",\n name: \"Historical Creative Quality\",\n description: \"Historical ad relevance quality component.\",\n category: \"quality\",\n format: \"enum\",\n apiField: \"metrics.historical_creative_quality_score\",\n type: \"api\",\n compatibleResources: [\"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"historicalLandingPageQualityScore\",\n name: \"Historical Landing Page Quality\",\n description: \"Historical landing page experience quality component.\",\n category: \"quality\",\n format: \"enum\",\n apiField: \"metrics.historical_landing_page_quality_score\",\n type: \"api\",\n compatibleResources: [\"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"historicalSearchPredictedCtr\",\n name: \"Historical Expected CTR\",\n description: \"Historical expected click-through rate quality component.\",\n category: \"quality\",\n format: \"enum\",\n apiField: \"metrics.historical_search_predicted_ctr\",\n type: \"api\",\n compatibleResources: [\"keyword_view\", \"ad_group_criterion\"],\n },\n];\n\n// ============================================\n// SHOPPING METRICS\n// Shopping uses standard metrics but on shopping_performance_view resource\n// ============================================\n\nconst SHOPPING_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"shoppingImpressions\",\n name: \"Shopping Impressions\",\n description: \"Number of times your Shopping ads were shown.\",\n category: \"shopping\",\n format: \"number\",\n apiField: \"metrics.impressions\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingClicks\",\n name: \"Shopping Clicks\",\n description: \"Number of clicks on your Shopping ads.\",\n category: \"shopping\",\n format: \"number\",\n apiField: \"metrics.clicks\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingCostMicros\",\n name: \"Shopping Cost\",\n description: \"Total cost of Shopping ad clicks in micro-currency.\",\n category: \"shopping\",\n format: \"micro_currency\",\n apiField: \"metrics.cost_micros\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingConversions\",\n name: \"Shopping Conversions\",\n description: \"Number of conversions from Shopping ads.\",\n category: \"shopping\",\n format: \"number\",\n apiField: \"metrics.conversions\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingConversionsValue\",\n name: \"Shopping Conversions Value\",\n description: \"Total value of conversions from Shopping ads.\",\n category: \"shopping\",\n format: \"currency\",\n apiField: \"metrics.conversions_value\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingCtr\",\n name: \"Shopping CTR\",\n description: \"Click-through rate for Shopping ads.\",\n category: \"shopping\",\n format: \"percentage\",\n apiField: \"metrics.ctr\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingAverageCpc\",\n name: \"Shopping Avg. CPC\",\n description: \"Average cost-per-click for Shopping ads.\",\n category: \"shopping\",\n format: \"micro_currency\",\n apiField: \"metrics.average_cpc\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingAllConversions\",\n name: \"Shopping All Conversions\",\n description: \"All conversions from Shopping ads including cross-device.\",\n category: \"shopping\",\n format: \"number\",\n apiField: \"metrics.all_conversions\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingAllConversionsValue\",\n name: \"Shopping All Conv. Value\",\n description: \"Total value of all conversions from Shopping ads.\",\n category: \"shopping\",\n format: \"currency\",\n apiField: \"metrics.all_conversions_value\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"shoppingCostPerConversion\",\n name: \"Shopping Cost / Conversion\",\n description: \"Average cost per conversion for Shopping ads.\",\n category: \"shopping\",\n format: \"micro_currency\",\n apiField: \"metrics.cost_per_conversion\",\n type: \"api\",\n compatibleResources: [\"shopping_performance_view\"],\n },\n];\n\n// ============================================\n// COMPETITIVE / AUCTION INSIGHT METRICS\n// Note: Auction insights are accessed via a special service,\n// not standard GAQL queries. These represent the fields returned.\n// ============================================\n\nconst COMPETITIVE_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"auctionInsightSearchImpressionShare\",\n name: \"Auction Insight: Search IS\",\n description: \"Your search impression share compared to competitors in auction insights.\",\n category: \"competitive\",\n format: \"percentage\",\n apiField: \"metrics.auction_insight_search_impression_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"auctionInsightSearchOverlapRate\",\n name: \"Auction Insight: Overlap Rate\",\n description: \"How often a competitor's ad received an impression when your ad also received one.\",\n category: \"competitive\",\n format: \"percentage\",\n apiField: \"metrics.auction_insight_search_overlap_rate\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"auctionInsightSearchPositionAboveRate\",\n name: \"Auction Insight: Position Above Rate\",\n description: \"How often a competitor's ad was shown above yours in the same auction.\",\n category: \"competitive\",\n format: \"percentage\",\n apiField: \"metrics.auction_insight_search_position_above_rate\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"auctionInsightSearchTopOfPageRate\",\n name: \"Auction Insight: Top of Page Rate\",\n description: \"How often a competitor's ad appeared at the top of the page.\",\n category: \"competitive\",\n format: \"percentage\",\n apiField: \"metrics.auction_insight_search_top_of_page_rate\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"auctionInsightSearchAbsoluteTopOfPageRate\",\n name: \"Auction Insight: Abs. Top Rate\",\n description: \"How often a competitor's ad appeared at the absolute top of the page.\",\n category: \"competitive\",\n format: \"percentage\",\n apiField: \"metrics.auction_insight_search_absolute_top_of_page_rate\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"auctionInsightSearchOutrankingShare\",\n name: \"Auction Insight: Outranking Share\",\n description: \"Percentage of auctions where your ad ranked higher than a competitor or showed when they did not.\",\n category: \"competitive\",\n format: \"percentage\",\n apiField: \"metrics.auction_insight_search_outranking_share\",\n type: \"api\",\n compatibleResources: [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n },\n];\n\n// ============================================\n// INVALID TRAFFIC METRICS\n// ============================================\n\nconst INVALID_TRAFFIC_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"invalidClicks\",\n name: \"Invalid Clicks\",\n description: \"Number of clicks Google considers illegitimate and does not charge for.\",\n category: \"invalid_traffic\",\n format: \"number\",\n apiField: \"metrics.invalid_clicks\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"ad_group_criterion\",\n \"customer\",\n ],\n },\n {\n key: \"invalidClickRate\",\n name: \"Invalid Click Rate\",\n description: \"Percentage of total clicks that are filtered as invalid.\",\n category: \"invalid_traffic\",\n format: \"percentage\",\n apiField: \"metrics.invalid_click_rate\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"ad_group_criterion\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// ACTIVE VIEW (VIEWABILITY) METRICS\n// ============================================\n\nconst ACTIVE_VIEW_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"activeViewCpm\",\n name: \"Active View CPM\",\n description: \"Average cost per thousand viewable impressions.\",\n category: \"core\",\n format: \"micro_currency\",\n apiField: \"metrics.active_view_cpm\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"activeViewCtr\",\n name: \"Active View CTR\",\n description: \"Clicks divided by viewable impressions.\",\n category: \"core\",\n format: \"percentage\",\n apiField: \"metrics.active_view_ctr\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"activeViewImpressions\",\n name: \"Active View Impressions\",\n description: \"Number of impressions that met Active View viewability criteria.\",\n category: \"core\",\n format: \"number\",\n apiField: \"metrics.active_view_impressions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"activeViewMeasurability\",\n name: \"Active View Measurability\",\n description: \"Ratio of impressions that could be measured by Active View.\",\n category: \"core\",\n format: \"percentage\",\n apiField: \"metrics.active_view_measurability\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"activeViewMeasurableImpressions\",\n name: \"Active View Measurable Impressions\",\n description: \"Number of impressions that were measurable by Active View.\",\n category: \"core\",\n format: \"number\",\n apiField: \"metrics.active_view_measurable_impressions\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"activeViewMeasurableCostMicros\",\n name: \"Active View Measurable Cost\",\n description: \"Cost of impressions that were measurable by Active View.\",\n category: \"core\",\n format: \"micro_currency\",\n apiField: \"metrics.active_view_measurable_cost_micros\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n {\n key: \"activeViewViewability\",\n name: \"Active View Viewability\",\n description: \"Percentage of measurable impressions that were actually viewable.\",\n category: \"core\",\n format: \"percentage\",\n apiField: \"metrics.active_view_viewability\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// CROSS-DEVICE METRICS\n// (cross_device_conversions is already in CONVERSION_METRICS)\n// These are additional cross-device specific metrics\n// ============================================\n\nconst CROSS_DEVICE_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"crossDeviceConversionsValueMicros\",\n name: \"Cross-Device Conv. Value\",\n description: \"Value of conversions where the user interacted on one device and converted on another.\",\n category: \"cross_device\",\n format: \"currency\",\n apiField: \"metrics.cross_device_conversions_value\",\n type: \"api\",\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// CALCULATED METRICS (derived client-side)\n// These are computed client-side from raw API data\n// ============================================\n\nconst CALCULATED_METRICS: GoogleAdsMetricDefinition[] = [\n {\n key: \"roas\",\n name: \"ROAS\",\n description: \"Return on ad spend: conversions value divided by cost.\",\n category: \"calculated\",\n format: \"ratio\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.conversions_value / (metrics.cost_micros / 1_000_000)\",\n dependencies: [\"conversionsValue\", \"costMicros\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsRoas\",\n name: \"All Conversions ROAS\",\n description: \"Return on ad spend for all conversions: all conversions value divided by cost.\",\n category: \"calculated\",\n format: \"ratio\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.all_conversions_value / (metrics.cost_micros / 1_000_000)\",\n dependencies: [\"allConversionsValue\", \"costMicros\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"customer\",\n ],\n },\n {\n key: \"valuePerConversion\",\n name: \"Value per Conversion\",\n description: \"Average value per conversion: conversions value divided by number of conversions.\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.conversions_value / metrics.conversions\",\n dependencies: [\"conversionsValue\", \"conversions\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"customer\",\n ],\n },\n {\n key: \"allConversionsValuePerConversion\",\n name: \"Value per All Conversion\",\n description: \"Average value per all conversions: all conversions value divided by all conversions.\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.all_conversions_value / metrics.all_conversions\",\n dependencies: [\"allConversionsValue\", \"allConversions\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"customer\",\n ],\n },\n {\n key: \"hookRate\",\n name: \"Hook Rate\",\n description: \"Proxy for video hook rate using the 25% quartile view rate.\",\n category: \"calculated\",\n format: \"percentage\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.video_quartile_p25_rate\",\n dependencies: [\"videoQuartileP25Rate\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"video\",\n ],\n },\n {\n key: \"holdRate\",\n name: \"Hold Rate\",\n description: \"Ratio of 50% video views to 25% video views, indicating mid-video retention.\",\n category: \"calculated\",\n format: \"percentage\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"(metrics.video_quartile_p50_rate / metrics.video_quartile_p25_rate) * 100\",\n dependencies: [\"videoQuartileP50Rate\", \"videoQuartileP25Rate\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"video\",\n ],\n },\n {\n key: \"completionRate\",\n name: \"Completion Rate\",\n description: \"Ratio of 100% video views to 25% video views, indicating full-video retention.\",\n category: \"calculated\",\n format: \"percentage\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"(metrics.video_quartile_p100_rate / metrics.video_quartile_p25_rate) * 100\",\n dependencies: [\"videoQuartileP100Rate\", \"videoQuartileP25Rate\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"video\",\n ],\n },\n {\n key: \"impressionShareLostTotal\",\n name: \"Search IS Lost (Total)\",\n description: \"Total search impression share lost, combining budget and rank lost shares.\",\n category: \"calculated\",\n format: \"percentage\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.search_budget_lost_impression_share + metrics.search_rank_lost_impression_share\",\n dependencies: [\"searchBudgetLostImpressionShare\", \"searchRankLostImpressionShare\"],\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"spendShare\",\n name: \"Spend Share\",\n description: \"Percentage of total spend allocated to this entity, computed across all rows.\",\n category: \"calculated\",\n format: \"percentage\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"(metrics.cost_micros / total_cost_micros) * 100\",\n dependencies: [\"costMicros\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"customer\",\n ],\n },\n {\n key: \"costPerConversionDollars\",\n name: \"Cost / Conversion ($)\",\n description: \"Cost per conversion in standard currency (converted from micros).\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.cost_micros / metrics.conversions / 1_000_000\",\n dependencies: [\"costMicros\", \"conversions\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"customer\",\n ],\n },\n {\n key: \"costDollars\",\n name: \"Cost ($)\",\n description: \"Total cost in standard currency (converted from micros).\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.cost_micros / 1_000_000\",\n dependencies: [\"costMicros\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"detail_placement_view\",\n \"topic_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n \"asset_group\",\n \"video\",\n \"customer\",\n ],\n },\n {\n key: \"averageCpcDollars\",\n name: \"Avg. CPC ($)\",\n description: \"Average cost-per-click in standard currency (converted from micros).\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.average_cpc / 1_000_000\",\n dependencies: [\"averageCpc\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"customer\",\n ],\n },\n {\n key: \"averageCpmDollars\",\n name: \"Avg. CPM ($)\",\n description: \"Average cost per thousand impressions in standard currency (converted from micros).\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"metrics.average_cpm / 1_000_000\",\n dependencies: [\"averageCpm\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"customer\",\n ],\n },\n];\n\n// ============================================\n// COMBINE ALL METRICS\n// ============================================\n\nexport const GOOGLE_ADS_METRIC_CATALOG: GoogleAdsMetricDefinition[] = [\n ...CORE_METRICS,\n ...SPEND_METRICS,\n ...CONVERSION_METRICS,\n ...IMPRESSION_SHARE_METRICS,\n ...VIDEO_METRICS,\n ...ENGAGEMENT_METRICS,\n ...QUALITY_METRICS,\n ...SHOPPING_METRICS,\n ...COMPETITIVE_METRICS,\n ...INVALID_TRAFFIC_METRICS,\n ...ACTIVE_VIEW_METRICS,\n ...CROSS_DEVICE_METRICS,\n ...CALCULATED_METRICS,\n];\n\n// ============================================\n// HELPER FUNCTIONS\n// ============================================\n\n/**\n * Get all Google Ads metrics\n */\nexport function getGoogleAdsMetrics(): GoogleAdsMetricDefinition[] {\n return GOOGLE_ADS_METRIC_CATALOG;\n}\n\n/**\n * Get metric definition by key\n */\nexport function getGoogleAdsMetricByKey(\n key: string\n): GoogleAdsMetricDefinition | undefined {\n return GOOGLE_ADS_METRIC_CATALOG.find((m) => m.key === key);\n}\n\n/**\n * Get metrics by category\n */\nexport function getGoogleAdsMetricsByCategory(\n category: GoogleAdsMetricCategory\n): GoogleAdsMetricDefinition[] {\n return GOOGLE_ADS_METRIC_CATALOG.filter((m) => m.category === category);\n}\n\n/**\n * Get API-only metrics (optionally filtered by keys)\n */\nexport function getGoogleAdsApiMetrics(\n keys?: string[]\n): GoogleAdsMetricDefinition[] {\n const apiMetrics = GOOGLE_ADS_METRIC_CATALOG.filter(\n (m) => m.type === \"api\"\n );\n if (!keys || keys.length === 0) return apiMetrics;\n return apiMetrics.filter((m) => keys.includes(m.key));\n}\n\n/**\n * Get calculated-only metrics (optionally filtered by keys)\n */\nexport function getGoogleAdsCalculatedMetrics(\n keys?: string[]\n): GoogleAdsMetricDefinition[] {\n const calcMetrics = GOOGLE_ADS_METRIC_CATALOG.filter(\n (m) => m.type === \"calculated\"\n );\n if (!keys || keys.length === 0) return calcMetrics;\n return calcMetrics.filter((m) => keys.includes(m.key));\n}\n\n/**\n * Convert metric keys to GAQL API fields.\n * For calculated metrics, includes the API fields of their dependencies.\n */\nexport function googleAdsMetricKeysToApiFields(keys: string[]): string[] {\n const apiFields = new Set<string>();\n\n for (const key of keys) {\n const metric = getGoogleAdsMetricByKey(key);\n if (!metric) continue;\n\n if (metric.type === \"api\" && metric.apiField) {\n apiFields.add(metric.apiField);\n } else if (metric.type === \"calculated\" && metric.dependencies) {\n // For calculated metrics, resolve the dependencies to their API fields\n for (const depKey of metric.dependencies) {\n const depMetric = getGoogleAdsMetricByKey(depKey);\n if (depMetric?.type === \"api\" && depMetric.apiField) {\n apiFields.add(depMetric.apiField);\n }\n }\n }\n }\n\n return Array.from(apiFields);\n}\n\n/**\n * Get metric definition by API field\n */\nexport function getGoogleAdsMetricByApiField(\n apiField: string\n): GoogleAdsMetricDefinition | undefined {\n return GOOGLE_ADS_METRIC_CATALOG.find(\n (m) => m.apiField === apiField && m.type === \"api\"\n );\n}\n\n/**\n * Get all metric categories present in the catalog\n */\nexport function getGoogleAdsMetricCategories(): GoogleAdsMetricCategory[] {\n return [...new Set(GOOGLE_ADS_METRIC_CATALOG.map((m) => m.category))];\n}\n\n/**\n * Get dependencies for calculated metrics\n */\nexport function getGoogleAdsMetricDependencies(keys: string[]): string[] {\n const dependencies = new Set<string>();\n\n for (const key of keys) {\n const metric = getGoogleAdsMetricByKey(key);\n if (metric?.type === \"calculated\" && metric.dependencies) {\n for (const dep of metric.dependencies) {\n dependencies.add(dep);\n }\n }\n }\n\n return Array.from(dependencies);\n}\n\n/**\n * Get count of metrics by category\n */\nexport function getGoogleAdsMetricCountByCategory(): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const metric of GOOGLE_ADS_METRIC_CATALOG) {\n counts[metric.category] = (counts[metric.category] || 0) + 1;\n }\n return counts;\n}\n\n/**\n * Get metrics compatible with a specific resource type\n */\nexport function getGoogleAdsMetricsForResource(\n resource: GoogleAdsResourceType\n): GoogleAdsMetricDefinition[] {\n return GOOGLE_ADS_METRIC_CATALOG.filter(\n (m) =>\n !m.compatibleResources || m.compatibleResources.includes(resource)\n );\n}\n\nexport default GOOGLE_ADS_METRIC_CATALOG;\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ADS DIMENSION CATALOG\n// Complete catalog of Google Ads dimensions\n// Including resource attributes and segments\n// ============================================\n\nimport {\n GoogleAdsDimensionDefinition,\n GoogleAdsDimensionCategory,\n} from \"./types.js\";\n\n// ============================================\n// ENTITY DIMENSIONS (Resource Attributes)\n// These are fields on resources, NOT segments\n// ============================================\n\nconst ENTITY_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"campaignId\",\n name: \"Campaign ID\",\n description: \"Unique identifier for the campaign\",\n category: \"entity\",\n apiField: \"campaign.id\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"campaign_criterion\",\n \"ad_group_criterion\",\n \"asset_group\",\n ],\n },\n {\n key: \"campaignName\",\n name: \"Campaign Name\",\n description: \"The name of the campaign\",\n category: \"entity\",\n apiField: \"campaign.name\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n \"geographic_view\",\n \"user_location_view\",\n \"landing_page_view\",\n \"campaign_audience_view\",\n \"ad_group_audience_view\",\n \"campaign_criterion\",\n \"ad_group_criterion\",\n \"asset_group\",\n ],\n },\n {\n key: \"campaignStatus\",\n name: \"Campaign Status\",\n description: \"Current status of the campaign (ENABLED, PAUSED, REMOVED)\",\n category: \"entity\",\n apiField: \"campaign.status\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\"ENABLED\", \"PAUSED\", \"REMOVED\", \"UNKNOWN\", \"UNSPECIFIED\"],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n ],\n },\n {\n key: \"campaignType\",\n name: \"Campaign Type\",\n description:\n \"The advertising channel type of the campaign (SEARCH, DISPLAY, SHOPPING, VIDEO, etc.)\",\n category: \"entity\",\n apiField: \"campaign.advertising_channel_type\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\n \"SEARCH\",\n \"DISPLAY\",\n \"SHOPPING\",\n \"VIDEO\",\n \"MULTI_CHANNEL\",\n \"PERFORMANCE_MAX\",\n \"DEMAND_GEN\",\n \"TRAVEL\",\n \"LOCAL\",\n \"SMART\",\n \"LOCAL_SERVICES\",\n \"UNKNOWN\",\n ],\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"shopping_performance_view\",\n ],\n },\n {\n key: \"campaignSubType\",\n name: \"Campaign Sub Type\",\n description: \"The advertising channel sub type of the campaign\",\n category: \"entity\",\n apiField: \"campaign.advertising_channel_sub_type\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n ],\n },\n {\n key: \"campaignBiddingStrategy\",\n name: \"Campaign Bidding Strategy\",\n description:\n \"The bidding strategy type used by the campaign (TARGET_CPA, MAXIMIZE_CONVERSIONS, etc.)\",\n category: \"bidding\",\n apiField: \"campaign.bidding_strategy_type\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\n \"TARGET_CPA\",\n \"TARGET_ROAS\",\n \"MAXIMIZE_CONVERSIONS\",\n \"MAXIMIZE_CONVERSION_VALUE\",\n \"MANUAL_CPC\",\n \"MANUAL_CPM\",\n \"MANUAL_CPV\",\n \"ENHANCED_CPC\",\n \"TARGET_IMPRESSION_SHARE\",\n \"TARGET_SPEND\",\n \"COMMISSION\",\n \"UNKNOWN\",\n ],\n compatibleResources: [\"campaign\", \"ad_group\", \"ad_group_ad\"],\n },\n {\n key: \"adGroupId\",\n name: \"Ad Group ID\",\n description: \"Unique identifier for the ad group\",\n category: \"entity\",\n apiField: \"ad_group.id\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n ],\n },\n {\n key: \"adGroupName\",\n name: \"Ad Group Name\",\n description: \"The name of the ad group\",\n category: \"entity\",\n apiField: \"ad_group.name\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n ],\n },\n {\n key: \"adGroupStatus\",\n name: \"Ad Group Status\",\n description: \"Current status of the ad group (ENABLED, PAUSED, REMOVED)\",\n category: \"entity\",\n apiField: \"ad_group.status\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\"ENABLED\", \"PAUSED\", \"REMOVED\", \"UNKNOWN\", \"UNSPECIFIED\"],\n compatibleResources: [\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n \"ad_group_audience_view\",\n \"ad_group_criterion\",\n ],\n },\n {\n key: \"adId\",\n name: \"Ad ID\",\n description: \"Unique identifier for the ad\",\n category: \"entity\",\n apiField: \"ad_group_ad.ad.id\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"ad_group_ad\"],\n },\n {\n key: \"adGroupAdAdId\",\n name: \"Ad Group Ad ID\",\n description: \"Alias for ad_group_ad.ad.id. Useful for agents that request fully-qualified ad_group_ad fields.\",\n category: \"entity\",\n apiField: \"ad_group_ad.ad.id\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"ad_group_ad\"],\n },\n {\n key: \"adGroupAdAdName\",\n name: \"Ad Group Ad Name\",\n description: \"Alias for ad_group_ad.ad.name when available from the Google Ads API.\",\n category: \"entity\",\n apiField: \"ad_group_ad.ad.name\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"ad_group_ad\"],\n },\n {\n key: \"adName\",\n name: \"Ad Name\",\n description: \"Ad name when available from ad_group_ad.ad.name.\",\n category: \"entity\",\n apiField: \"ad_group_ad.ad.name\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"ad_group_ad\"],\n },\n {\n key: \"adType\",\n name: \"Ad Type\",\n description:\n \"The type of the ad (RESPONSIVE_SEARCH_AD, RESPONSIVE_DISPLAY_AD, VIDEO_AD, etc.)\",\n category: \"ad_format\",\n apiField: \"ad_group_ad.ad.type\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\n \"RESPONSIVE_SEARCH_AD\",\n \"RESPONSIVE_DISPLAY_AD\",\n \"EXPANDED_TEXT_AD\",\n \"VIDEO_AD\",\n \"IMAGE_AD\",\n \"CALL_AD\",\n \"SHOPPING_PRODUCT_AD\",\n \"SHOPPING_SMART_AD\",\n \"APP_AD\",\n \"APP_ENGAGEMENT_AD\",\n \"DISCOVERY_MULTI_ASSET_AD\",\n \"DISCOVERY_CAROUSEL_AD\",\n \"UNKNOWN\",\n ],\n compatibleResources: [\"ad_group_ad\"],\n },\n {\n key: \"keywordText\",\n name: \"Keyword Text\",\n description: \"The text of the keyword criterion (e.g., 'polo ralph lauren')\",\n category: \"entity\",\n apiField: \"ad_group_criterion.keyword.text\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"keywordMatchType\",\n name: \"Keyword Match Type\",\n description: \"The match type of the keyword (EXACT, PHRASE, BROAD)\",\n category: \"entity\",\n apiField: \"ad_group_criterion.keyword.match_type\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\"EXACT\", \"PHRASE\", \"BROAD\", \"UNKNOWN\", \"UNSPECIFIED\"],\n compatibleResources: [\"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"qualityScore\",\n name: \"Quality Score\",\n description: \"The current quality score of the keyword (1-10). Only available on keyword_view.\",\n category: \"entity\",\n apiField: \"ad_group_criterion.quality_info.quality_score\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"keyword_view\", \"ad_group_criterion\"],\n },\n {\n key: \"customerId\",\n name: \"Customer ID\",\n description: \"The Google Ads account (customer) ID\",\n category: \"entity\",\n apiField: \"customer.id\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\n \"customer\",\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n ],\n },\n {\n key: \"customerName\",\n name: \"Customer Name\",\n description: \"The descriptive name of the Google Ads account\",\n category: \"entity\",\n apiField: \"customer.descriptive_name\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\n \"customer\",\n \"campaign\",\n \"ad_group\",\n \"ad_group_ad\",\n \"keyword_view\",\n \"search_term_view\",\n ],\n },\n];\n\n// ============================================\n// TIME SEGMENTS\n// ============================================\n\nconst TIME_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"date\",\n name: \"Date\",\n description: \"The date of the data point (YYYY-MM-DD format)\",\n category: \"time\",\n apiField: \"segments.date\",\n isSegment: true,\n isResourceAttribute: false,\n },\n {\n key: \"dayOfWeek\",\n name: \"Day of Week\",\n description: \"The day of the week (MONDAY through SUNDAY)\",\n category: \"time\",\n apiField: \"segments.day_of_week\",\n isSegment: true,\n isResourceAttribute: false,\n possibleValues: [\n \"MONDAY\",\n \"TUESDAY\",\n \"WEDNESDAY\",\n \"THURSDAY\",\n \"FRIDAY\",\n \"SATURDAY\",\n \"SUNDAY\",\n ],\n },\n {\n key: \"hour\",\n name: \"Hour of Day\",\n description:\n \"The hour of the day (0-23). Requires date segment to be present.\",\n category: \"time\",\n apiField: \"segments.hour\",\n isSegment: true,\n isResourceAttribute: false,\n requiresSegment: \"date\",\n possibleValues: [\n \"0\",\n \"1\",\n \"2\",\n \"3\",\n \"4\",\n \"5\",\n \"6\",\n \"7\",\n \"8\",\n \"9\",\n \"10\",\n \"11\",\n \"12\",\n \"13\",\n \"14\",\n \"15\",\n \"16\",\n \"17\",\n \"18\",\n \"19\",\n \"20\",\n \"21\",\n \"22\",\n \"23\",\n ],\n },\n {\n key: \"month\",\n name: \"Month\",\n description: \"The month of the data point (YYYY-MM format)\",\n category: \"time\",\n apiField: \"segments.month\",\n isSegment: true,\n isResourceAttribute: false,\n },\n {\n key: \"week\",\n name: \"Week\",\n description:\n \"The week of the data point (date of the Monday of the week, YYYY-MM-DD format)\",\n category: \"time\",\n apiField: \"segments.week\",\n isSegment: true,\n isResourceAttribute: false,\n },\n {\n key: \"quarter\",\n name: \"Quarter\",\n description:\n \"The quarter of the data point (first day of the quarter, YYYY-MM-DD format)\",\n category: \"time\",\n apiField: \"segments.quarter\",\n isSegment: true,\n isResourceAttribute: false,\n },\n {\n key: \"year\",\n name: \"Year\",\n description: \"The year of the data point\",\n category: \"time\",\n apiField: \"segments.year\",\n isSegment: true,\n isResourceAttribute: false,\n },\n];\n\n// ============================================\n// DEVICE SEGMENTS\n// ============================================\n\nconst DEVICE_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"device\",\n name: \"Device\",\n description:\n \"The device type where the impression or interaction occurred\",\n category: \"device\",\n apiField: \"segments.device\",\n isSegment: true,\n isResourceAttribute: false,\n possibleValues: [\n \"MOBILE\",\n \"DESKTOP\",\n \"TABLET\",\n \"CONNECTED_TV\",\n \"OTHER\",\n \"UNKNOWN\",\n \"UNSPECIFIED\",\n ],\n },\n {\n key: \"slot\",\n name: \"Ad Slot\",\n description: \"The position on the page where the ad was shown\",\n category: \"device\",\n apiField: \"segments.slot\",\n isSegment: true,\n isResourceAttribute: false,\n possibleValues: [\n \"SEARCH_TOP\",\n \"SEARCH_OTHER\",\n \"CONTENT\",\n \"MIXED\",\n \"UNSPECIFIED\",\n ],\n },\n];\n\n// ============================================\n// NETWORK SEGMENTS\n// ============================================\n\nconst NETWORK_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"adNetworkType\",\n name: \"Ad Network Type\",\n description:\n \"The ad network where the impression was served (Search, Display, YouTube, etc.)\",\n category: \"network\",\n apiField: \"segments.ad_network_type\",\n isSegment: true,\n isResourceAttribute: false,\n possibleValues: [\n \"SEARCH\",\n \"SEARCH_PARTNERS\",\n \"CONTENT\",\n \"YOUTUBE_SEARCH\",\n \"YOUTUBE_WATCH\",\n \"MIXED\",\n \"CROSS_NETWORK\",\n \"UNKNOWN\",\n \"UNSPECIFIED\",\n ],\n },\n];\n\n// ============================================\n// CONVERSION SEGMENTS\n// ============================================\n\nconst CONVERSION_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"conversionAction\",\n name: \"Conversion Action\",\n description:\n \"The conversion action resource name. Segments data by individual conversion actions. Incompatible with many impression share metrics.\",\n category: \"conversion\",\n apiField: \"segments.conversion_action\",\n isSegment: true,\n isResourceAttribute: false,\n incompatibleWith: [\n \"searchImpressionShare\",\n \"searchBudgetLostImpressionShare\",\n \"searchRankLostImpressionShare\",\n \"searchTopImpressionShare\",\n \"searchAbsoluteTopImpressionShare\",\n \"searchExactMatchImpressionShare\",\n \"contentImpressionShare\",\n \"contentBudgetLostImpressionShare\",\n \"contentRankLostImpressionShare\",\n ],\n },\n {\n key: \"conversionActionName\",\n name: \"Conversion Action Name\",\n description: \"The human-readable name of the conversion action\",\n category: \"conversion\",\n apiField: \"segments.conversion_action_name\",\n isSegment: true,\n isResourceAttribute: false,\n incompatibleWith: [\n \"searchImpressionShare\",\n \"searchBudgetLostImpressionShare\",\n \"searchRankLostImpressionShare\",\n \"searchTopImpressionShare\",\n \"searchAbsoluteTopImpressionShare\",\n \"searchExactMatchImpressionShare\",\n \"contentImpressionShare\",\n \"contentBudgetLostImpressionShare\",\n \"contentRankLostImpressionShare\",\n ],\n },\n {\n key: \"conversionActionCategory\",\n name: \"Conversion Action Category\",\n description:\n \"The category of the conversion action (PURCHASE, LEAD, SIGNUP, etc.)\",\n category: \"conversion\",\n apiField: \"segments.conversion_action_category\",\n isSegment: true,\n isResourceAttribute: false,\n incompatibleWith: [\n \"searchImpressionShare\",\n \"searchBudgetLostImpressionShare\",\n \"searchRankLostImpressionShare\",\n \"searchTopImpressionShare\",\n \"searchAbsoluteTopImpressionShare\",\n \"searchExactMatchImpressionShare\",\n \"contentImpressionShare\",\n \"contentBudgetLostImpressionShare\",\n \"contentRankLostImpressionShare\",\n ],\n },\n {\n key: \"externalConversionSource\",\n name: \"External Conversion Source\",\n description:\n \"The external source of the conversion (GOOGLE_ANALYTICS, FIREBASE, UPLOAD, etc.)\",\n category: \"conversion\",\n apiField: \"segments.external_conversion_source\",\n isSegment: true,\n isResourceAttribute: false,\n incompatibleWith: [\n \"searchImpressionShare\",\n \"searchBudgetLostImpressionShare\",\n \"searchRankLostImpressionShare\",\n \"searchTopImpressionShare\",\n \"searchAbsoluteTopImpressionShare\",\n \"searchExactMatchImpressionShare\",\n \"contentImpressionShare\",\n \"contentBudgetLostImpressionShare\",\n \"contentRankLostImpressionShare\",\n ],\n },\n];\n\n// ============================================\n// DEMOGRAPHIC SEGMENTS\n// ============================================\n\nconst DEMOGRAPHIC_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"ageRange\",\n name: \"Age Range\",\n description: \"The age range of the user who saw or interacted with the ad\",\n category: \"demographic\",\n apiField: \"segments.age_range\",\n isSegment: true,\n isResourceAttribute: false,\n possibleValues: [\n \"AGE_RANGE_18_24\",\n \"AGE_RANGE_25_34\",\n \"AGE_RANGE_35_44\",\n \"AGE_RANGE_45_54\",\n \"AGE_RANGE_55_64\",\n \"AGE_RANGE_65_UP\",\n \"AGE_RANGE_UNDETERMINED\",\n \"UNKNOWN\",\n \"UNSPECIFIED\",\n ],\n },\n {\n key: \"gender\",\n name: \"Gender\",\n description: \"The gender of the user who saw or interacted with the ad\",\n category: \"demographic\",\n apiField: \"segments.gender\",\n isSegment: true,\n isResourceAttribute: false,\n possibleValues: [\"MALE\", \"FEMALE\", \"UNDETERMINED\", \"UNKNOWN\", \"UNSPECIFIED\"],\n },\n];\n\n// ============================================\n// GEOGRAPHIC SEGMENTS\n// ============================================\n\nconst GEOGRAPHIC_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"geoTargetCountry\",\n name: \"Geo Target Country\",\n description:\n \"The country geo target constant resource name for geographic segmentation\",\n category: \"geographic\",\n apiField: \"segments.geo_target_country\",\n isSegment: true,\n isResourceAttribute: false,\n },\n {\n key: \"geoTargetRegion\",\n name: \"Geo Target Region\",\n description:\n \"The region (state/province) geo target constant resource name for geographic segmentation\",\n category: \"geographic\",\n apiField: \"segments.geo_target_region\",\n isSegment: true,\n isResourceAttribute: false,\n },\n {\n key: \"geoTargetMetro\",\n name: \"Geo Target Metro\",\n description:\n \"The metro area (DMA) geo target constant resource name for geographic segmentation\",\n category: \"geographic\",\n apiField: \"segments.geo_target_metro\",\n isSegment: true,\n isResourceAttribute: false,\n },\n];\n\n// ============================================\n// CAMPAIGN TYPE ATTRIBUTES\n// ============================================\n\nconst CAMPAIGN_TYPE_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"advertisingChannelType\",\n name: \"Advertising Channel Type\",\n description:\n \"The advertising channel type of the campaign (SEARCH, SHOPPING, PERFORMANCE_MAX, etc.). Alias for campaignType.\",\n category: \"campaign_type\",\n apiField: \"campaign.advertising_channel_type\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\n \"SEARCH\",\n \"DISPLAY\",\n \"SHOPPING\",\n \"VIDEO\",\n \"MULTI_CHANNEL\",\n \"PERFORMANCE_MAX\",\n \"DEMAND_GEN\",\n \"TRAVEL\",\n \"LOCAL\",\n \"SMART\",\n \"LOCAL_SERVICES\",\n \"UNKNOWN\",\n ],\n },\n {\n key: \"advertisingChannelSubType\",\n name: \"Advertising Channel Sub Type\",\n description:\n \"The advertising channel sub type of the campaign. Alias for campaignSubType.\",\n category: \"campaign_type\",\n apiField: \"campaign.advertising_channel_sub_type\",\n isSegment: false,\n isResourceAttribute: true,\n },\n];\n\n// ============================================\n// SHOPPING / PRODUCT DIMENSIONS (Segments)\n// Available on shopping_performance_view\n// ============================================\n\nconst SHOPPING_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"productItemId\",\n name: \"Product Item ID (SKU)\",\n description: \"The Merchant Center Item ID (SKU) of the product\",\n category: \"shopping\",\n apiField: \"segments.product_item_id\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productBrand\",\n name: \"Product Brand\",\n description: \"The brand of the product from the Merchant Center feed\",\n category: \"shopping\",\n apiField: \"segments.product_brand\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productTitle\",\n name: \"Product Title\",\n description: \"The title of the product from the Merchant Center feed\",\n category: \"shopping\",\n apiField: \"segments.product_title\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"productCategoryLevel1\",\n name: \"Product Category (L1)\",\n description: \"Google product taxonomy level 1 (e.g., Apparel & Accessories)\",\n category: \"shopping\",\n apiField: \"segments.product_category_level1\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCategoryLevel2\",\n name: \"Product Category (L2)\",\n description: \"Google product taxonomy level 2\",\n category: \"shopping\",\n apiField: \"segments.product_category_level2\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCategoryLevel3\",\n name: \"Product Category (L3)\",\n description: \"Google product taxonomy level 3\",\n category: \"shopping\",\n apiField: \"segments.product_category_level3\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCategoryLevel4\",\n name: \"Product Category (L4)\",\n description: \"Google product taxonomy level 4\",\n category: \"shopping\",\n apiField: \"segments.product_category_level4\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCategoryLevel5\",\n name: \"Product Category (L5)\",\n description: \"Google product taxonomy level 5\",\n category: \"shopping\",\n apiField: \"segments.product_category_level5\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productTypeL1\",\n name: \"Product Type (L1)\",\n description: \"Merchant-defined product type level 1\",\n category: \"shopping\",\n apiField: \"segments.product_type_l1\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productTypeL2\",\n name: \"Product Type (L2)\",\n description: \"Merchant-defined product type level 2\",\n category: \"shopping\",\n apiField: \"segments.product_type_l2\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productTypeL3\",\n name: \"Product Type (L3)\",\n description: \"Merchant-defined product type level 3\",\n category: \"shopping\",\n apiField: \"segments.product_type_l3\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productTypeL4\",\n name: \"Product Type (L4)\",\n description: \"Merchant-defined product type level 4\",\n category: \"shopping\",\n apiField: \"segments.product_type_l4\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productTypeL5\",\n name: \"Product Type (L5)\",\n description: \"Merchant-defined product type level 5\",\n category: \"shopping\",\n apiField: \"segments.product_type_l5\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCustomAttribute0\",\n name: \"Custom Label 0\",\n description: \"Custom label 0 from Merchant Center feed\",\n category: \"shopping\",\n apiField: \"segments.product_custom_attribute0\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCustomAttribute1\",\n name: \"Custom Label 1\",\n description: \"Custom label 1 from Merchant Center feed\",\n category: \"shopping\",\n apiField: \"segments.product_custom_attribute1\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCustomAttribute2\",\n name: \"Custom Label 2\",\n description: \"Custom label 2 from Merchant Center feed\",\n category: \"shopping\",\n apiField: \"segments.product_custom_attribute2\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCustomAttribute3\",\n name: \"Custom Label 3\",\n description: \"Custom label 3 from Merchant Center feed\",\n category: \"shopping\",\n apiField: \"segments.product_custom_attribute3\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productCustomAttribute4\",\n name: \"Custom Label 4\",\n description: \"Custom label 4 from Merchant Center feed\",\n category: \"shopping\",\n apiField: \"segments.product_custom_attribute4\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\", \"asset_group_listing_group_filter\"],\n },\n {\n key: \"productChannel\",\n name: \"Product Channel\",\n description: \"Whether the product is sold online or locally\",\n category: \"shopping\",\n apiField: \"segments.product_channel\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n possibleValues: [\"ONLINE\", \"LOCAL\"],\n },\n {\n key: \"productChannelExclusivity\",\n name: \"Product Channel Exclusivity\",\n description: \"Whether the product is sold exclusively in one channel\",\n category: \"shopping\",\n apiField: \"segments.product_channel_exclusivity\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n possibleValues: [\"SINGLE_CHANNEL\", \"MULTI_CHANNEL\"],\n },\n {\n key: \"productCondition\",\n name: \"Product Condition\",\n description: \"Condition of the product from the feed\",\n category: \"shopping\",\n apiField: \"segments.product_condition\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n possibleValues: [\"NEW\", \"REFURBISHED\", \"USED\"],\n },\n {\n key: \"productCountry\",\n name: \"Product Country\",\n description: \"The target country of the product in the Merchant Center feed\",\n category: \"shopping\",\n apiField: \"segments.product_country\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"productLanguage\",\n name: \"Product Language\",\n description: \"The language of the product listing\",\n category: \"shopping\",\n apiField: \"segments.product_language\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"productStoreId\",\n name: \"Product Store ID\",\n description: \"The store ID for Local Inventory Ads\",\n category: \"shopping\",\n apiField: \"segments.product_store_id\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"productAggregatorId\",\n name: \"Product Aggregator ID\",\n description: \"The aggregator ID of the product (for multi-client accounts)\",\n category: \"shopping\",\n apiField: \"segments.product_aggregator_id\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n },\n {\n key: \"productMerchantId\",\n name: \"Product Merchant ID\",\n description: \"The Merchant Center ID of the product\",\n category: \"shopping\",\n apiField: \"segments.product_merchant_id\",\n isSegment: true,\n isResourceAttribute: false,\n compatibleResources: [\"shopping_performance_view\"],\n },\n];\n\n// ============================================\n// BUDGET DIMENSIONS (campaign_budget resource attributes)\n// ============================================\n\nconst BUDGET_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"campaignBudgetAmount\",\n name: \"Daily Budget\",\n description: \"Daily budget amount in micro-currency (divide by 1,000,000 for standard currency). Shows the configured daily budget.\",\n category: \"budget\",\n apiField: \"campaign_budget.amount_micros\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\", \"campaign_budget\"],\n },\n {\n key: \"campaignBudgetTotal\",\n name: \"Total Budget (Lifetime)\",\n description: \"Total lifetime budget in micro-currency. Only set for campaigns with lifetime budgets (period = CUSTOM_PERIOD).\",\n category: \"budget\",\n apiField: \"campaign_budget.total_amount_micros\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\", \"campaign_budget\"],\n },\n {\n key: \"campaignBudgetPeriod\",\n name: \"Budget Period\",\n description: \"Whether the budget is daily or lifetime (DAILY or CUSTOM_PERIOD)\",\n category: \"budget\",\n apiField: \"campaign_budget.period\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\"DAILY\", \"CUSTOM_PERIOD\", \"UNKNOWN\", \"UNSPECIFIED\"],\n compatibleResources: [\"campaign\", \"campaign_budget\"],\n },\n {\n key: \"campaignBudgetDeliveryMethod\",\n name: \"Budget Delivery Method\",\n description: \"How the budget is spent across the day (STANDARD or ACCELERATED)\",\n category: \"budget\",\n apiField: \"campaign_budget.delivery_method\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\"STANDARD\", \"ACCELERATED\", \"UNKNOWN\", \"UNSPECIFIED\"],\n compatibleResources: [\"campaign\", \"campaign_budget\"],\n },\n {\n key: \"campaignBudgetExplicitlyShared\",\n name: \"Budget Shared\",\n description: \"Whether this budget is shared across multiple campaigns\",\n category: \"budget\",\n apiField: \"campaign_budget.explicitly_shared\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\", \"campaign_budget\"],\n },\n];\n\n// ============================================\n// BIDDING TARGET DIMENSIONS (campaign resource attributes)\n// ============================================\n\nconst BIDDING_TARGET_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"campaignTargetRoas\",\n name: \"Target ROAS\",\n description: \"Target return on ad spend value. Set when using TARGET_ROAS or MAXIMIZE_CONVERSION_VALUE with target ROAS.\",\n category: \"bidding\",\n apiField: \"campaign.target_roas.target_roas\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignMaximizeConversionValueTargetRoas\",\n name: \"Max Conv. Value Target ROAS\",\n description: \"Target ROAS for MAXIMIZE_CONVERSION_VALUE bidding strategy.\",\n category: \"bidding\",\n apiField: \"campaign.maximize_conversion_value.target_roas\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignTargetCpaMicros\",\n name: \"Target CPA\",\n description: \"Target cost-per-acquisition in micro-currency. Set when using TARGET_CPA bidding strategy.\",\n category: \"bidding\",\n apiField: \"campaign.target_cpa.target_cpa_micros\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignMaximizeConversionsTargetCpaMicros\",\n name: \"Max Conv. Target CPA\",\n description: \"Optional target CPA for MAXIMIZE_CONVERSIONS bidding strategy in micro-currency.\",\n category: \"bidding\",\n apiField: \"campaign.maximize_conversions.target_cpa_micros\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignTargetImpressionShareLocation\",\n name: \"Target IS Location\",\n description: \"Where to target impressions for TARGET_IMPRESSION_SHARE strategy (ANYWHERE_ON_PAGE, TOP_OF_PAGE, ABSOLUTE_TOP_OF_PAGE).\",\n category: \"bidding\",\n apiField: \"campaign.target_impression_share.location\",\n isSegment: false,\n isResourceAttribute: true,\n possibleValues: [\"ANYWHERE_ON_PAGE\", \"TOP_OF_PAGE\", \"ABSOLUTE_TOP_OF_PAGE\", \"UNKNOWN\", \"UNSPECIFIED\"],\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignTargetImpressionShareFraction\",\n name: \"Target IS Fraction\",\n description: \"Target percentage of impressions for TARGET_IMPRESSION_SHARE (in micros, divide by 1,000,000).\",\n category: \"bidding\",\n apiField: \"campaign.target_impression_share.location_fraction_micros\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignTargetImpressionShareCpcCeiling\",\n name: \"Target IS CPC Ceiling\",\n description: \"Maximum CPC bid ceiling for TARGET_IMPRESSION_SHARE in micro-currency.\",\n category: \"bidding\",\n apiField: \"campaign.target_impression_share.cpc_bid_ceiling_micros\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignOptimizationScore\",\n name: \"Optimization Score\",\n description: \"Campaign optimization score between 0 and 1 (multiply by 100 for percentage).\",\n category: \"bidding\",\n apiField: \"campaign.optimization_score\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignStartDate\",\n name: \"Campaign Start Date\",\n description: \"The start date of the campaign in YYYY-MM-DD format.\",\n category: \"entity\",\n apiField: \"campaign.start_date\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n {\n key: \"campaignEndDate\",\n name: \"Campaign End Date\",\n description: \"The end date of the campaign in YYYY-MM-DD format (if set).\",\n category: \"entity\",\n apiField: \"campaign.end_date\",\n isSegment: false,\n isResourceAttribute: true,\n compatibleResources: [\"campaign\"],\n },\n];\n\n// ============================================\n// INTERACTION DIMENSIONS (click_type, etc.)\n// ============================================\n\nconst INTERACTION_DIMENSIONS: GoogleAdsDimensionDefinition[] = [\n {\n key: \"clickType\",\n name: \"Click Type\",\n description: \"Type of click (headline, sitelink, call, etc.)\",\n category: \"interaction\",\n apiField: \"segments.click_type\",\n isSegment: true,\n isResourceAttribute: false,\n incompatibleWith: [\n \"conversionAction\",\n \"conversionActionName\",\n \"conversionActionCategory\",\n \"externalConversionSource\",\n ],\n possibleValues: [\n \"URL_CLICKS\",\n \"CALLS\",\n \"APP_DEEP_LINK\",\n \"WEBSITE\",\n \"PRODUCT_LISTING_AD_CLICKS\",\n \"SITELINKS\",\n \"GET_DIRECTIONS\",\n \"OFFER_PRINTS\",\n \"BREADCRUMBS\",\n \"CALL_TRACKING\",\n \"MOBILE_CALL_TRACKING\",\n \"LOCATION_EXPANSION\",\n \"STORE_LOCATOR\",\n \"VIDEO_WEBSITE_CLICKS\",\n \"VIDEO_CALL_TO_ACTION_CLICKS\",\n \"VIDEO_APP_STORE_CLICKS\",\n \"VIDEO_CARD_ACTION_HEADLINE_CLICKS\",\n \"VIDEO_END_CAP_CLICKS\",\n ],\n },\n {\n key: \"adDestinationType\",\n name: \"Ad Destination Type\",\n description: \"The destination type that users interact with\",\n category: \"interaction\",\n apiField: \"segments.ad_destination_type\",\n isSegment: true,\n isResourceAttribute: false,\n },\n {\n key: \"interactionOnThisExtension\",\n name: \"Interaction on This Extension\",\n description: \"Whether the interaction happened on this specific extension vs. the ad itself\",\n category: \"interaction\",\n apiField: \"segments.interaction_on_this_extension\",\n isSegment: true,\n isResourceAttribute: false,\n },\n];\n\n// ============================================\n// COMBINE ALL DIMENSIONS\n// ============================================\n\nexport const GOOGLE_ADS_DIMENSION_CATALOG: GoogleAdsDimensionDefinition[] = [\n ...ENTITY_DIMENSIONS,\n ...TIME_DIMENSIONS,\n ...DEVICE_DIMENSIONS,\n ...NETWORK_DIMENSIONS,\n ...CONVERSION_DIMENSIONS,\n ...DEMOGRAPHIC_DIMENSIONS,\n ...GEOGRAPHIC_DIMENSIONS,\n ...CAMPAIGN_TYPE_DIMENSIONS,\n ...BUDGET_DIMENSIONS,\n ...BIDDING_TARGET_DIMENSIONS,\n ...SHOPPING_DIMENSIONS,\n ...INTERACTION_DIMENSIONS,\n];\n\n// ============================================\n// HELPER FUNCTIONS\n// ============================================\n\n/**\n * Get all Google Ads dimensions\n */\nexport function getGoogleAdsDimensions(): GoogleAdsDimensionDefinition[] {\n return GOOGLE_ADS_DIMENSION_CATALOG;\n}\n\n/**\n * Get dimension definition by key\n */\nexport function getGoogleAdsDimensionByKey(\n key: string\n): GoogleAdsDimensionDefinition | undefined {\n return GOOGLE_ADS_DIMENSION_CATALOG.find((d) => d.key === key);\n}\n\n/**\n * Get dimensions by category\n */\nexport function getGoogleAdsDimensionsByCategory(\n category: GoogleAdsDimensionCategory\n): GoogleAdsDimensionDefinition[] {\n return GOOGLE_ADS_DIMENSION_CATALOG.filter((d) => d.category === category);\n}\n\n/**\n * Get only segment dimensions (isSegment=true)\n */\nexport function getGoogleAdsSegments(): GoogleAdsDimensionDefinition[] {\n return GOOGLE_ADS_DIMENSION_CATALOG.filter((d) => d.isSegment === true);\n}\n\n/**\n * Get only resource attribute dimensions (isResourceAttribute=true)\n */\nexport function getGoogleAdsResourceAttributes(): GoogleAdsDimensionDefinition[] {\n return GOOGLE_ADS_DIMENSION_CATALOG.filter(\n (d) => d.isResourceAttribute === true\n );\n}\n\n/**\n * Convert dimension keys to their corresponding GAQL API fields\n */\nexport function googleAdsDimensionKeysToApiFields(keys: string[]): string[] {\n return keys\n .map((key) => {\n const dimension = getGoogleAdsDimensionByKey(key);\n return dimension?.apiField || null;\n })\n .filter((field): field is string => field !== null);\n}\n\nexport default GOOGLE_ADS_DIMENSION_CATALOG;\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ADS COMPATIBILITY RULES\n// Validation logic for metric/segment/resource combinations\n// ============================================\n\nimport { GoogleAdsResourceType } from \"./types.js\";\nimport { GOOGLE_ADS_DIMENSION_CATALOG } from \"./dimension-catalog.js\";\n\n// ============================================\n// RESOURCE-METRIC COMPATIBILITY\n// ============================================\n\n/**\n * Metrics that are only available on specific resources\n */\nexport const RESOURCE_RESTRICTED_METRICS: Record<string, GoogleAdsResourceType[]> = {\n // Quality score only on keyword_view (via ad_group_criterion)\n \"metrics.historical_quality_score\": [\"keyword_view\"],\n \"metrics.historical_creative_quality_score\": [\"keyword_view\"],\n \"metrics.historical_landing_page_quality_score\": [\"keyword_view\"],\n \"metrics.historical_search_predicted_ctr\": [\"keyword_view\"],\n\n // Video quartile metrics not on keyword_view\n \"metrics.video_quartile_p25_rate\": [\"campaign\", \"ad_group\", \"ad_group_ad\", \"video\"],\n \"metrics.video_quartile_p50_rate\": [\"campaign\", \"ad_group\", \"ad_group_ad\", \"video\"],\n \"metrics.video_quartile_p75_rate\": [\"campaign\", \"ad_group\", \"ad_group_ad\", \"video\"],\n \"metrics.video_quartile_p100_rate\": [\"campaign\", \"ad_group\", \"ad_group_ad\", \"video\"],\n \"metrics.video_trueview_views\": [\"campaign\", \"ad_group\", \"ad_group_ad\", \"video\"],\n \"metrics.video_trueview_view_rate\": [\"campaign\", \"ad_group\", \"ad_group_ad\", \"video\"],\n};\n\n/**\n * Impression share metrics with their allowed resources\n * (NOT limited to campaign -- API v23 supports ad_group, keyword_view, ad_group_criterion too)\n */\nexport const IMPRESSION_SHARE_METRICS: Record<string, GoogleAdsResourceType[]> = {\n \"metrics.search_impression_share\": [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n \"metrics.search_budget_lost_impression_share\": [\"campaign\"],\n \"metrics.search_rank_lost_impression_share\": [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n \"metrics.search_top_impression_share\": [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n \"metrics.search_absolute_top_impression_share\": [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n \"metrics.search_exact_match_impression_share\": [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n \"metrics.search_budget_lost_absolute_top_impression_share\": [\"campaign\"],\n \"metrics.search_budget_lost_top_impression_share\": [\"campaign\"],\n \"metrics.search_rank_lost_absolute_top_impression_share\": [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n \"metrics.search_rank_lost_top_impression_share\": [\"campaign\", \"ad_group\", \"keyword_view\", \"ad_group_criterion\"],\n \"metrics.content_impression_share\": [\"campaign\", \"ad_group\"],\n \"metrics.content_budget_lost_impression_share\": [\"campaign\"],\n \"metrics.content_rank_lost_impression_share\": [\"campaign\", \"ad_group\"],\n};\n\n// ============================================\n// SEGMENT COMPATIBILITY\n// ============================================\n\n/**\n * Segments that cannot be used together\n */\nexport const INCOMPATIBLE_SEGMENT_PAIRS: Array<[string, string]> = [\n // click_type cannot combine with conversion segments\n [\"segments.click_type\", \"segments.conversion_action\"],\n [\"segments.click_type\", \"segments.conversion_action_name\"],\n [\"segments.click_type\", \"segments.conversion_action_category\"],\n // date-dependent segments\n // No explicit incompatibilities beyond hour requiring date\n];\n\n/**\n * Segments that require another segment to be present\n */\nexport const SEGMENT_DEPENDENCIES: Record<string, string> = {\n \"segments.hour\": \"segments.date\",\n};\n\n/**\n * Segments that are incompatible with impression share metrics\n */\nexport const IMPRESSION_SHARE_INCOMPATIBLE_SEGMENTS = [\n \"segments.conversion_action\",\n \"segments.conversion_action_name\",\n \"segments.conversion_action_category\",\n \"segments.external_conversion_source\",\n \"segments.click_type\",\n \"segments.ad_destination_type\",\n];\n\n/**\n * Shopping product segments -- only valid on these resources\n */\nconst SHOPPING_SEGMENT_ALLOWED_RESOURCES: GoogleAdsResourceType[] = [\n \"shopping_performance_view\",\n \"asset_group_listing_group_filter\",\n];\n\n/**\n * All shopping product segment fields\n */\nconst SHOPPING_PRODUCT_SEGMENTS = [\n \"segments.product_item_id\",\n \"segments.product_brand\",\n \"segments.product_title\",\n \"segments.product_category_level1\",\n \"segments.product_category_level2\",\n \"segments.product_category_level3\",\n \"segments.product_category_level4\",\n \"segments.product_category_level5\",\n \"segments.product_type_l1\",\n \"segments.product_type_l2\",\n \"segments.product_type_l3\",\n \"segments.product_type_l4\",\n \"segments.product_type_l5\",\n \"segments.product_custom_attribute0\",\n \"segments.product_custom_attribute1\",\n \"segments.product_custom_attribute2\",\n \"segments.product_custom_attribute3\",\n \"segments.product_custom_attribute4\",\n \"segments.product_channel\",\n \"segments.product_channel_exclusivity\",\n \"segments.product_condition\",\n \"segments.product_country\",\n \"segments.product_language\",\n \"segments.product_store_id\",\n \"segments.product_aggregator_id\",\n \"segments.product_merchant_id\",\n];\n\n/**\n * Segments incompatible with specific resources (non-shopping restrictions)\n */\nexport const RESOURCE_INCOMPATIBLE_SEGMENTS: Record<string, string[]> = {\n customer: [\n \"segments.conversion_action\",\n \"segments.conversion_action_name\",\n \"segments.age_range\",\n \"segments.gender\",\n ],\n shopping_performance_view: [\n \"segments.age_range\",\n \"segments.gender\",\n \"segments.slot\",\n ],\n // PMax asset group has limited segment support\n asset_group: [\n \"segments.conversion_action\",\n \"segments.conversion_action_name\",\n \"segments.click_type\",\n \"segments.product_item_id\",\n \"segments.product_brand\",\n ],\n};\n\n// ============================================\n// VALIDATION FUNCTIONS\n// ============================================\n\nexport interface ValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\n/**\n * Validate a query selection (metrics + segments + resource)\n */\nexport function validateQuerySelection(\n metricApiFields: string[],\n segmentApiFields: string[],\n resource: GoogleAdsResourceType,\n dimensionKeys?: string[]\n): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // 1. Check resource-restricted metrics\n for (const metric of metricApiFields) {\n const allowedResources = RESOURCE_RESTRICTED_METRICS[metric];\n if (allowedResources && !allowedResources.includes(resource)) {\n errors.push(\n `Metric \"${metric}\" is not available on resource \"${resource}\". Available on: ${allowedResources.join(\", \")}`\n );\n }\n }\n\n // 2. Check impression share metrics against their per-metric allowed resources\n for (const metric of metricApiFields) {\n const allowedResources = IMPRESSION_SHARE_METRICS[metric];\n if (allowedResources && !allowedResources.includes(resource)) {\n errors.push(\n `Metric \"${metric}\" is not available on resource \"${resource}\". Available on: ${allowedResources.join(\", \")}`\n );\n }\n }\n\n const hasImpressionShareMetrics = metricApiFields.some((m) => m in IMPRESSION_SHARE_METRICS);\n\n // 3. Check segment dependencies\n for (const segment of segmentApiFields) {\n const required = SEGMENT_DEPENDENCIES[segment];\n if (required && !segmentApiFields.includes(required)) {\n errors.push(\n `Segment \"${segment}\" requires \"${required}\" to also be selected`\n );\n }\n }\n\n // 4. Check incompatible segment pairs\n for (const [seg1, seg2] of INCOMPATIBLE_SEGMENT_PAIRS) {\n if (segmentApiFields.includes(seg1) && segmentApiFields.includes(seg2)) {\n errors.push(\n `Segments \"${seg1}\" and \"${seg2}\" cannot be used together`\n );\n }\n }\n\n // 5. Check impression share metrics with incompatible segments\n if (hasImpressionShareMetrics) {\n const incompatibleSegments = segmentApiFields.filter((s) =>\n IMPRESSION_SHARE_INCOMPATIBLE_SEGMENTS.includes(s)\n );\n if (incompatibleSegments.length > 0) {\n // This is a warning, not error -- the query planner will split the query\n warnings.push(\n `Impression share metrics are incompatible with segments: ${incompatibleSegments.join(\", \")}. Query will be split automatically.`\n );\n }\n }\n\n // 6. Check shopping segments against resource (ERROR, not warning)\n const selectedShoppingSegments = segmentApiFields.filter((s) =>\n SHOPPING_PRODUCT_SEGMENTS.includes(s)\n );\n if (selectedShoppingSegments.length > 0 && !SHOPPING_SEGMENT_ALLOWED_RESOURCES.includes(resource)) {\n errors.push(\n `Shopping segments (${selectedShoppingSegments.join(\", \")}) are only available on: ${SHOPPING_SEGMENT_ALLOWED_RESOURCES.join(\", \")}. Current resource: \"${resource}\"`\n );\n }\n\n // 7. Check other resource-incompatible segments\n const restricted = RESOURCE_INCOMPATIBLE_SEGMENTS[resource];\n if (restricted) {\n const offending = segmentApiFields.filter((s) => restricted.includes(s));\n if (offending.length > 0) {\n errors.push(\n `Segments ${offending.join(\", \")} are not compatible with resource \"${resource}\"`\n );\n }\n }\n\n // 8. Validate dimension compatibility with resource\n if (dimensionKeys && dimensionKeys.length > 0) {\n for (const dimKey of dimensionKeys) {\n const dim = GOOGLE_ADS_DIMENSION_CATALOG.find((d) => d.key === dimKey);\n if (dim?.compatibleResources && dim.compatibleResources.length > 0) {\n if (!dim.compatibleResources.includes(resource)) {\n warnings.push(\n `Dimension \"${dim.name}\" (${dim.apiField}) may not be available on resource \"${resource}\"`\n );\n }\n }\n }\n }\n\n // 9. Info about aggregated vs daily data\n if (!segmentApiFields.includes(\"segments.date\") && !segmentApiFields.includes(\"segments.month\") && !segmentApiFields.includes(\"segments.week\")) {\n warnings.push(\n \"No time segment (date/week/month) selected -- results will be aggregated over the full date range. Add segments.date for daily breakdown.\"\n );\n }\n\n // 10. Warn about large result sets\n if (segmentApiFields.length > 3) {\n warnings.push(\n \"Multiple segments selected -- this may produce a very large number of rows\"\n );\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Check if a metric needs to be split into a separate query\n */\nexport function requiresSeparateQuery(\n metricApiField: string,\n _otherMetrics: string[]\n): boolean {\n // Auction insight metrics require a completely different query\n if (metricApiField.startsWith(\"metrics.auction_insight\")) {\n return true;\n }\n return false;\n}\n\n/**\n * Get all incompatible segments for a given set of metrics\n */\nexport function getIncompatibleSegments(metricApiFields: string[]): string[] {\n const incompatible = new Set<string>();\n\n const hasImpressionShare = metricApiFields.some((m) => m in IMPRESSION_SHARE_METRICS);\n\n if (hasImpressionShare) {\n for (const seg of IMPRESSION_SHARE_INCOMPATIBLE_SEGMENTS) {\n incompatible.add(seg);\n }\n }\n\n return Array.from(incompatible);\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ADS FILTER CATALOG\n// Definitions for GAQL WHERE clause filters\n// ============================================\n\nimport {\n GoogleAdsFilterDefinition,\n GoogleAdsFilterOperator,\n} from \"./types.js\";\n\n// ============================================\n// FILTER CATALOG\n// ============================================\n\nexport const GOOGLE_ADS_FILTER_CATALOG: GoogleAdsFilterDefinition[] = [\n // ========================================\n // CAMPAIGN STATUS FILTERS\n // ========================================\n {\n key: \"campaignStatus\",\n name: \"Campaign Status\",\n description: \"Filter by campaign status\",\n apiField: \"campaign.status\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"enum\",\n enumValues: [\"ENABLED\", \"PAUSED\", \"REMOVED\"],\n defaultOperator: \"=\",\n defaultValue: \"ENABLED\",\n },\n {\n key: \"adGroupStatus\",\n name: \"Ad Group Status\",\n description: \"Filter by ad group status\",\n apiField: \"ad_group.status\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"enum\",\n enumValues: [\"ENABLED\", \"PAUSED\", \"REMOVED\"],\n defaultOperator: \"=\",\n defaultValue: \"ENABLED\",\n },\n {\n key: \"adStatus\",\n name: \"Ad Status\",\n description: \"Filter by ad status\",\n apiField: \"ad_group_ad.status\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"enum\",\n enumValues: [\"ENABLED\", \"PAUSED\", \"REMOVED\"],\n defaultOperator: \"=\",\n defaultValue: \"ENABLED\",\n },\n\n // ========================================\n // CAMPAIGN TYPE FILTERS\n // ========================================\n {\n key: \"channelType\",\n name: \"Campaign Type\",\n description: \"Filter by advertising channel type\",\n apiField: \"campaign.advertising_channel_type\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"enum\",\n enumValues: [\n \"SEARCH\",\n \"DISPLAY\",\n \"SHOPPING\",\n \"VIDEO\",\n \"MULTI_CHANNEL\",\n \"PERFORMANCE_MAX\",\n \"DEMAND_GEN\",\n \"TRAVEL\",\n \"LOCAL\",\n \"SMART\",\n \"LOCAL_SERVICES\",\n ],\n defaultOperator: \"=\",\n },\n {\n key: \"channelSubType\",\n name: \"Campaign Sub Type\",\n description: \"Filter by advertising channel sub type\",\n apiField: \"campaign.advertising_channel_sub_type\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"enum\",\n enumValues: [\n \"SEARCH_MOBILE_APP\",\n \"DISPLAY_MOBILE_APP\",\n \"SEARCH_EXPRESS\",\n \"DISPLAY_EXPRESS\",\n \"SHOPPING_SMART_ADS\",\n \"DISPLAY_GMAIL_AD\",\n \"DISPLAY_SMART_CAMPAIGN\",\n \"VIDEO_OUTSTREAM\",\n \"VIDEO_ACTION\",\n \"VIDEO_NON_SKIPPABLE\",\n \"VIDEO_REACH_TARGET_FREQUENCY\",\n \"APP_CAMPAIGN\",\n \"APP_CAMPAIGN_FOR_ENGAGEMENT\",\n \"LOCAL_CAMPAIGN\",\n \"SHOPPING_COMPARISON_LISTING_ADS\",\n \"SMART_CAMPAIGN\",\n \"VIDEO_SEQUENCE\",\n \"APP_CAMPAIGN_FOR_PRE_REGISTRATION\",\n \"TRAVEL_ACTIVITIES\",\n ],\n defaultOperator: \"=\",\n },\n\n // ========================================\n // BIDDING STRATEGY FILTERS\n // ========================================\n {\n key: \"biddingStrategyType\",\n name: \"Bidding Strategy Type\",\n description: \"Filter by bidding strategy type\",\n apiField: \"campaign.bidding_strategy_type\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"enum\",\n enumValues: [\n \"TARGET_CPA\",\n \"TARGET_ROAS\",\n \"MAXIMIZE_CONVERSIONS\",\n \"MAXIMIZE_CONVERSION_VALUE\",\n \"MANUAL_CPC\",\n \"MANUAL_CPM\",\n \"MANUAL_CPV\",\n \"ENHANCED_CPC\",\n \"TARGET_IMPRESSION_SHARE\",\n \"TARGET_SPEND\",\n ],\n defaultOperator: \"=\",\n },\n\n // ========================================\n // METRIC FILTERS\n // ========================================\n {\n key: \"impressionsGt\",\n name: \"Impressions >\",\n description: \"Filter rows with impressions greater than value\",\n apiField: \"metrics.impressions\",\n operators: [\">\", \">=\", \"<\", \"<=\", \"=\"],\n type: \"number\",\n defaultOperator: \">\",\n defaultValue: \"0\",\n },\n {\n key: \"clicksGt\",\n name: \"Clicks >\",\n description: \"Filter rows with clicks greater than value\",\n apiField: \"metrics.clicks\",\n operators: [\">\", \">=\", \"<\", \"<=\", \"=\"],\n type: \"number\",\n defaultOperator: \">\",\n defaultValue: \"0\",\n },\n {\n key: \"costGt\",\n name: \"Cost >\",\n description: \"Filter rows with cost greater than value (in micros)\",\n apiField: \"metrics.cost_micros\",\n operators: [\">\", \">=\", \"<\", \"<=\", \"=\"],\n type: \"number\",\n defaultOperator: \">\",\n defaultValue: \"0\",\n },\n {\n key: \"conversionsGt\",\n name: \"Conversions >\",\n description: \"Filter rows with conversions greater than value\",\n apiField: \"metrics.conversions\",\n operators: [\">\", \">=\", \"<\", \"<=\", \"=\"],\n type: \"number\",\n defaultOperator: \">\",\n defaultValue: \"0\",\n },\n\n // ========================================\n // ENTITY NAME FILTERS\n // ========================================\n {\n key: \"campaignName\",\n name: \"Campaign Name\",\n description: \"Filter by campaign name\",\n apiField: \"campaign.name\",\n operators: [\"=\", \"!=\", \"LIKE\", \"NOT LIKE\", \"CONTAINS ANY\"],\n type: \"string\",\n defaultOperator: \"LIKE\",\n },\n {\n key: \"adGroupName\",\n name: \"Ad Group Name\",\n description: \"Filter by ad group name\",\n apiField: \"ad_group.name\",\n operators: [\"=\", \"!=\", \"LIKE\", \"NOT LIKE\", \"CONTAINS ANY\"],\n type: \"string\",\n defaultOperator: \"LIKE\",\n },\n\n // ========================================\n // KEYWORD FILTERS\n // ========================================\n {\n key: \"keywordMatchType\",\n name: \"Keyword Match Type\",\n description: \"Filter by keyword match type\",\n apiField: \"ad_group_criterion.keyword.match_type\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"enum\",\n enumValues: [\"EXACT\", \"PHRASE\", \"BROAD\"],\n defaultOperator: \"=\",\n },\n {\n key: \"keywordText\",\n name: \"Keyword Text\",\n description: \"Filter by keyword text\",\n apiField: \"ad_group_criterion.keyword.text\",\n operators: [\"=\", \"!=\", \"LIKE\", \"NOT LIKE\", \"CONTAINS ANY\"],\n type: \"string\",\n defaultOperator: \"LIKE\",\n },\n\n // ========================================\n // LABEL FILTERS\n // ========================================\n {\n key: \"campaignLabels\",\n name: \"Campaign Labels\",\n description: \"Filter by campaign label resource names\",\n apiField: \"campaign.labels\",\n operators: [\"CONTAINS ANY\", \"CONTAINS ALL\", \"CONTAINS NONE\"],\n type: \"string\",\n defaultOperator: \"CONTAINS ANY\",\n },\n\n // ========================================\n // CAMPAIGN ID FILTERS\n // ========================================\n {\n key: \"campaignId\",\n name: \"Campaign ID\",\n description: \"Filter by specific campaign ID\",\n apiField: \"campaign.id\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"number\",\n defaultOperator: \"=\",\n },\n {\n key: \"adGroupId\",\n name: \"Ad Group ID\",\n description: \"Filter by specific ad group ID\",\n apiField: \"ad_group.id\",\n operators: [\"=\", \"!=\", \"IN\", \"NOT IN\"],\n type: \"number\",\n defaultOperator: \"=\",\n },\n];\n\n// ============================================\n// HELPER FUNCTIONS\n// ============================================\n\nexport function getGoogleAdsFilters(): GoogleAdsFilterDefinition[] {\n return GOOGLE_ADS_FILTER_CATALOG;\n}\n\nexport function getGoogleAdsFilterByKey(key: string): GoogleAdsFilterDefinition | undefined {\n return GOOGLE_ADS_FILTER_CATALOG.find((f) => f.key === key);\n}\n\n/**\n * Build a GAQL WHERE clause fragment from a filter\n */\nexport function buildFilterClause(\n apiField: string,\n operator: GoogleAdsFilterOperator,\n value: string | string[] | number | number[]\n): string {\n // Handle special operators\n if (operator === \"IS NULL\" || operator === \"IS NOT NULL\") {\n return `${apiField} ${operator}`;\n }\n\n if (operator === \"DURING\") {\n return `${apiField} DURING ${value}`;\n }\n\n if (operator === \"BETWEEN\") {\n if (Array.isArray(value) && value.length === 2) {\n return `${apiField} BETWEEN '${value[0]}' AND '${value[1]}'`;\n }\n return \"\";\n }\n\n // Handle IN/NOT IN operators\n if (operator === \"IN\" || operator === \"NOT IN\" || operator === \"CONTAINS ANY\" || operator === \"CONTAINS ALL\" || operator === \"CONTAINS NONE\") {\n const values = Array.isArray(value) ? value : [value];\n const formatted = values.map((v) =>\n typeof v === \"number\" ? String(v) : `'${v}'`\n );\n return `${apiField} ${operator} (${formatted.join(\", \")})`;\n }\n\n // Handle LIKE/NOT LIKE\n if (operator === \"LIKE\" || operator === \"NOT LIKE\") {\n return `${apiField} ${operator} '%${value}%'`;\n }\n\n // Standard comparison\n if (typeof value === \"number\") {\n return `${apiField} ${operator} ${value}`;\n }\n\n // String enum values should be unquoted\n const enumFields = [\n \"campaign.status\",\n \"ad_group.status\",\n \"ad_group_ad.status\",\n \"campaign.advertising_channel_type\",\n \"campaign.advertising_channel_sub_type\",\n \"campaign.bidding_strategy_type\",\n \"ad_group_criterion.keyword.match_type\",\n ];\n\n if (enumFields.includes(apiField)) {\n return `${apiField} ${operator} '${value}'`;\n }\n\n return `${apiField} ${operator} '${value}'`;\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ADS GAQL QUERY PLANNER\n// ============================================\n// Plans, validates, and builds GAQL queries\n// Handles metric/segment splitting for incompatible combinations\n\nimport type {\n GoogleAdsQueryRequest,\n GoogleAdsQueryPlan,\n GoogleAdsGaqlQuery,\n GoogleAdsResourceType,\n} from \"./types.js\";\nimport { GOOGLE_ADS_METRIC_CATALOG } from \"./metric-catalog.js\";\nimport { GOOGLE_ADS_DIMENSION_CATALOG } from \"./dimension-catalog.js\";\nimport { validateQuerySelection } from \"./compatibility-rules.js\";\nimport { buildFilterClause } from \"./filter-catalog.js\";\n\n// ============================================\n// HELPERS -- Resolve catalog keys to API fields\n// ============================================\n\n/**\n * Convert snake_case to camelCase: \"average_cpc\" -> \"averageCpc\"\n */\nfunction snakeToCamel(s: string): string {\n return s.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());\n}\n\n/**\n * Special aliases for metric names that don't follow simple snake->camel rules.\n * The copilot system prompt tells Claude to use \"cost\" (not \"costMicros\"),\n * since the backend auto-converts micros to dollars.\n */\nconst METRIC_KEY_ALIASES: Record<string, string> = {\n cost: \"costMicros\",\n cost_micros: \"costMicros\",\n};\n\n/**\n * Find a metric in the catalog using flexible key matching:\n * 1. Exact catalog key match (e.g., \"costMicros\")\n * 2. Alias lookup (e.g., \"cost\" -> \"costMicros\")\n * 3. snake_case -> camelCase conversion (e.g., \"average_cpc\" -> \"averageCpc\")\n * 4. API field match with \"metrics.\" prefix (e.g., \"cost_micros\" -> \"metrics.cost_micros\")\n * 5. Exact API field match (e.g., \"metrics.cost_micros\")\n */\nfunction findMetricByKey(key: string) {\n // 1. Exact catalog key match\n let metric = GOOGLE_ADS_METRIC_CATALOG.find((m) => m.key === key);\n if (metric) return metric;\n\n // 2. Alias lookup\n const aliased = METRIC_KEY_ALIASES[key];\n if (aliased) {\n metric = GOOGLE_ADS_METRIC_CATALOG.find((m) => m.key === aliased);\n if (metric) return metric;\n }\n\n // 3. snake_case -> camelCase conversion\n const camelKey = snakeToCamel(key);\n if (camelKey !== key) {\n metric = GOOGLE_ADS_METRIC_CATALOG.find((m) => m.key === camelKey);\n if (metric) return metric;\n }\n\n // 4. API field match with \"metrics.\" prefix\n const withPrefix = key.startsWith(\"metrics.\") ? key : `metrics.${key}`;\n metric = GOOGLE_ADS_METRIC_CATALOG.find((m) => m.apiField === withPrefix);\n if (metric) return metric;\n\n // 5. Exact API field match (for keys already containing \"metrics.\")\n if (key.startsWith(\"metrics.\")) {\n metric = GOOGLE_ADS_METRIC_CATALOG.find((m) => m.apiField === key);\n if (metric) return metric;\n }\n\n return null;\n}\n\n/**\n * Find a dimension in the catalog using flexible key matching:\n * 1. Exact catalog key match (e.g., \"date\", \"campaignName\")\n * 2. snake_case -> camelCase conversion (e.g., \"campaign_name\" -> \"campaignName\")\n * 3. Exact API field match (e.g., \"segments.date\", \"campaign.name\")\n */\nfunction findDimensionByKey(key: string) {\n // 1. Exact catalog key\n let dim = GOOGLE_ADS_DIMENSION_CATALOG.find((d) => d.key === key);\n if (dim) return dim;\n\n // 2. snake_case -> camelCase\n const camelKey = snakeToCamel(key);\n if (camelKey !== key) {\n dim = GOOGLE_ADS_DIMENSION_CATALOG.find((d) => d.key === camelKey);\n if (dim) return dim;\n }\n\n // 3. Exact API field match (handles \"segments.date\", \"campaign.name\", etc.)\n dim = GOOGLE_ADS_DIMENSION_CATALOG.find((d) => d.apiField === key);\n if (dim) return dim;\n\n return null;\n}\n\nexport function resolveMetricApiField(key: string): string | null {\n return findMetricByKey(key)?.apiField || null;\n}\n\nfunction resolveDimensionApiField(key: string): string | null {\n return findDimensionByKey(key)?.apiField || null;\n}\n\nfunction isCalculatedMetric(key: string): boolean {\n const metric = findMetricByKey(key);\n return metric?.type === \"calculated\";\n}\n\nfunction getMetricDependencies(key: string): string[] {\n const metric = findMetricByKey(key);\n return metric?.dependencies || [];\n}\n\nfunction getMetricIncompatibleSegments(key: string): string[] {\n const metric = findMetricByKey(key);\n return metric?.incompatibleSegments || [];\n}\n\nconst TIME_SEGMENT_FIELDS = new Set([\n \"segments.date\",\n \"segments.week\",\n \"segments.month\",\n \"segments.quarter\",\n \"segments.year\",\n]);\n\nfunction hasTimeSegment(segmentApiFields: string[]): boolean {\n return segmentApiFields.some((field) => TIME_SEGMENT_FIELDS.has(field));\n}\n\nfunction defaultMetricOrderBy(\n explicitOrderBy: string | undefined,\n limit: number | undefined,\n metricApiFields: string[],\n segmentApiFields: string[]\n): string | undefined {\n if (explicitOrderBy) return explicitOrderBy;\n if (!limit || limit <= 0) return undefined;\n if (metricApiFields.length === 0) return undefined;\n if (hasTimeSegment(segmentApiFields)) return undefined;\n\n return metricApiFields[0];\n}\n\n// ============================================\n// GAQL BUILDER\n// ============================================\n\nfunction buildGaql(\n selectFields: string[],\n resource: GoogleAdsResourceType,\n whereClause: string[],\n orderBy?: string,\n orderDirection?: \"ASC\" | \"DESC\",\n limit?: number\n): string {\n const parts: string[] = [];\n\n // SELECT\n parts.push(`SELECT ${selectFields.join(\", \")}`);\n\n // FROM\n parts.push(`FROM ${resource}`);\n\n // WHERE\n if (whereClause.length > 0) {\n parts.push(`WHERE ${whereClause.join(\" AND \")}`);\n }\n\n // ORDER BY\n if (orderBy) {\n parts.push(`ORDER BY ${orderBy} ${orderDirection || \"DESC\"}`);\n }\n\n // LIMIT\n if (limit && limit > 0) {\n parts.push(`LIMIT ${limit}`);\n }\n\n return parts.join(\"\\n\");\n}\n\n// ============================================\n// DATE WHERE CLAUSE\n// ============================================\n\nfunction formatDate(date: Date): string {\n return date.toISOString().slice(0, 10);\n}\n\nfunction inclusiveLastNDaysRange(days: number): { startDate: string; endDate: string } {\n const end = new Date();\n end.setUTCHours(0, 0, 0, 0);\n end.setUTCDate(end.getUTCDate() - 1);\n\n const start = new Date(end);\n start.setUTCDate(start.getUTCDate() - (days - 1));\n\n return {\n startDate: formatDate(start),\n endDate: formatDate(end),\n };\n}\n\nfunction buildDateWhereClause(\n startDate?: string,\n endDate?: string,\n datePreset?: string\n): { clause: string | null; warnings: string[] } {\n if (datePreset) {\n if (datePreset === \"LAST_90_DAYS\") {\n const range = inclusiveLastNDaysRange(90);\n return {\n clause: `segments.date BETWEEN '${range.startDate}' AND '${range.endDate}'`,\n warnings: [\"LAST_90_DAYS is not a valid GAQL DURING literal; translated to an explicit 90-day BETWEEN range.\"],\n };\n }\n\n return { clause: `segments.date DURING ${datePreset}`, warnings: [] };\n }\n\n if (startDate && endDate) {\n return { clause: `segments.date BETWEEN '${startDate}' AND '${endDate}'`, warnings: [] };\n }\n\n return { clause: null, warnings: [] };\n}\n\n// ============================================\n// MAIN QUERY PLANNER\n// ============================================\n\nexport function planQuery(request: GoogleAdsQueryRequest): GoogleAdsQueryPlan {\n const {\n resource,\n metrics,\n dimensions = [],\n filters = [],\n startDate,\n endDate,\n datePreset,\n orderBy,\n orderDirection,\n limit,\n } = request;\n\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // 1. Separate calculated vs API metrics\n const calculatedMetricKeys = metrics.filter((k) => isCalculatedMetric(k));\n const apiMetricKeys = metrics.filter((k) => !isCalculatedMetric(k));\n\n // 2. Resolve calculated metric dependencies -> add to API metrics\n const dependencyApiFields = new Set<string>();\n for (const calcKey of calculatedMetricKeys) {\n for (const depKey of getMetricDependencies(calcKey)) {\n const field = resolveMetricApiField(depKey);\n if (field) {\n dependencyApiFields.add(field);\n }\n }\n }\n\n // 3. Resolve API metric keys to fields\n const metricApiFields: string[] = [];\n for (const key of apiMetricKeys) {\n const field = resolveMetricApiField(key);\n if (field) {\n metricApiFields.push(field);\n } else {\n warnings.push(`Metric \"${key}\" not found in catalog, skipping`);\n }\n }\n\n // Add dependency fields not already in the list\n for (const depField of dependencyApiFields) {\n if (!metricApiFields.includes(depField)) {\n metricApiFields.push(depField);\n }\n }\n\n // 4. Resolve dimension keys to fields\n const dimensionApiFields: string[] = [];\n const segmentApiFields: string[] = [];\n\n for (const key of dimensions) {\n const dim = findDimensionByKey(key);\n if (!dim) {\n warnings.push(`Dimension \"${key}\" not found in catalog, skipping`);\n continue;\n }\n dimensionApiFields.push(dim.apiField);\n if (dim.isSegment) {\n segmentApiFields.push(dim.apiField);\n }\n }\n\n // 5. Validate metric/segment/resource compatibility\n const validation = validateQuerySelection(\n metricApiFields,\n segmentApiFields,\n resource,\n dimensions\n );\n\n if (!validation.valid) {\n return {\n queries: [],\n mergeStrategy: \"none\",\n joinKeys: [],\n warnings: validation.warnings,\n errors: validation.errors,\n estimatedApiCalls: 0,\n calculatedMetrics: calculatedMetricKeys,\n };\n }\n\n warnings.push(...validation.warnings);\n\n // 6. Check for incompatible metric/segment combinations that need splitting\n const incompatibleMetricKeys: string[] = [];\n const compatibleMetricKeys: string[] = [];\n\n for (const key of apiMetricKeys) {\n const incompatSegs = getMetricIncompatibleSegments(key);\n const hasConflict = segmentApiFields.some((seg) =>\n incompatSegs.includes(seg)\n );\n if (hasConflict) {\n incompatibleMetricKeys.push(key);\n } else {\n compatibleMetricKeys.push(key);\n }\n }\n\n // 7. Build WHERE clauses\n const whereClause: string[] = [];\n\n // Date filter\n const dateClause = buildDateWhereClause(startDate, endDate, datePreset);\n warnings.push(...dateClause.warnings);\n if (dateClause.clause) {\n // Ensure segments.date is in SELECT when using date filter\n if (!dimensionApiFields.includes(\"segments.date\") && !datePreset) {\n // Date filter doesn't require segments.date in SELECT\n // GAQL allows filtering on segments.date without selecting it\n }\n whereClause.push(dateClause.clause);\n }\n\n // User filters\n for (const filter of filters) {\n const clause = buildFilterClause(\n filter.field,\n filter.operator,\n filter.value\n );\n if (clause) {\n whereClause.push(clause);\n }\n }\n\n // When LIMIT is present without an explicit ORDER BY, Google Ads can return\n // old paused entities first, making active accounts look empty. Use the first\n // selected metric as a stable default for non-time-series top lists.\n const effectiveOrderBy = defaultMetricOrderBy(\n orderBy,\n limit,\n metricApiFields,\n segmentApiFields\n );\n const effectiveOrderDirection = orderBy ? orderDirection : \"DESC\";\n\n // 8. Build GAQL queries\n const queries: GoogleAdsGaqlQuery[] = [];\n\n if (incompatibleMetricKeys.length > 0 && compatibleMetricKeys.length > 0) {\n // SPLIT: Two queries -- compatible metrics with all dims, incompatible metrics without conflicting segments\n const compatibleFields = compatibleMetricKeys\n .map(resolveMetricApiField)\n .filter((f): f is string => f !== null);\n\n // Also add dependency fields to compatible query\n const compatibleWithDeps = [...compatibleFields];\n for (const depField of dependencyApiFields) {\n if (!compatibleWithDeps.includes(depField)) {\n compatibleWithDeps.push(depField);\n }\n }\n\n // Query 1: Compatible metrics with all dimensions\n const selectFields1 = [...dimensionApiFields, ...compatibleWithDeps];\n if (selectFields1.length > 0) {\n queries.push({\n gaql: buildGaql(\n selectFields1,\n resource,\n whereClause,\n effectiveOrderBy,\n effectiveOrderDirection,\n limit\n ),\n resource,\n selectFields: selectFields1,\n metrics: compatibleMetricKeys,\n dimensions,\n description: `Main query with ${compatibleMetricKeys.length} compatible metrics`,\n });\n }\n\n // Query 2: Incompatible metrics without conflicting segments\n const incompatibleFields = incompatibleMetricKeys\n .map(resolveMetricApiField)\n .filter((f): f is string => f !== null);\n\n // Find safe dimensions (remove conflicting segments)\n const allIncompatSegs = new Set<string>();\n for (const key of incompatibleMetricKeys) {\n for (const seg of getMetricIncompatibleSegments(key)) {\n allIncompatSegs.add(seg);\n }\n }\n\n const safeDimensionFields = dimensionApiFields.filter(\n (d) => !allIncompatSegs.has(d)\n );\n const safeDimensionKeys = dimensions.filter((key) => {\n const dim = findDimensionByKey(key);\n return dim && !allIncompatSegs.has(dim.apiField);\n });\n\n const selectFields2 = [...safeDimensionFields, ...incompatibleFields];\n if (selectFields2.length > 0) {\n queries.push({\n gaql: buildGaql(\n selectFields2,\n resource,\n whereClause,\n effectiveOrderBy,\n effectiveOrderDirection,\n limit\n ),\n resource,\n selectFields: selectFields2,\n metrics: incompatibleMetricKeys,\n dimensions: safeDimensionKeys,\n description: `Split query for ${incompatibleMetricKeys.length} metrics incompatible with selected segments`,\n });\n\n warnings.push(\n `Query was split into ${queries.length} requests due to metric/segment incompatibilities`\n );\n }\n } else {\n // SINGLE query -- all metrics and dimensions are compatible\n const allMetricFields = metricApiFields;\n const selectFields = [...dimensionApiFields, ...allMetricFields];\n\n if (selectFields.length === 0) {\n errors.push(\"No metrics or dimensions selected\");\n return {\n queries: [],\n mergeStrategy: \"none\",\n joinKeys: [],\n warnings,\n errors,\n estimatedApiCalls: 0,\n calculatedMetrics: calculatedMetricKeys,\n };\n }\n\n queries.push({\n gaql: buildGaql(\n selectFields,\n resource,\n whereClause,\n effectiveOrderBy,\n effectiveOrderDirection,\n limit\n ),\n resource,\n selectFields,\n metrics: apiMetricKeys,\n dimensions,\n description: `Query ${resource} with ${metricApiFields.length} metrics and ${dimensionApiFields.length} dimensions`,\n });\n }\n\n // 9. Determine merge strategy\n let mergeStrategy: \"join\" | \"union\" | \"none\" = \"none\";\n const joinKeys: string[] = [];\n\n if (queries.length > 1) {\n mergeStrategy = \"join\";\n // Join on shared dimension fields\n const firstQueryDims = new Set(queries[0].dimensions);\n const secondQueryDims = new Set(queries[1].dimensions);\n for (const dim of firstQueryDims) {\n if (secondQueryDims.has(dim)) {\n const field = resolveDimensionApiField(dim);\n if (field) joinKeys.push(field);\n }\n }\n // If no shared dims, fall back to union\n if (joinKeys.length === 0) {\n mergeStrategy = \"union\";\n }\n }\n\n return {\n queries,\n mergeStrategy,\n joinKeys,\n warnings,\n errors,\n estimatedApiCalls: queries.length,\n calculatedMetrics: calculatedMetricKeys,\n };\n}\n\n// ============================================\n// QUERY PREVIEW (for debug UI)\n// ============================================\n\nexport function generateQueryPreview(plan: GoogleAdsQueryPlan): string {\n const lines: string[] = [];\n\n lines.push(\"=== Google Ads Query Plan ===\");\n lines.push(`Total Queries: ${plan.queries.length}`);\n lines.push(`Merge Strategy: ${plan.mergeStrategy}`);\n if (plan.joinKeys.length > 0) {\n lines.push(`Join Keys: ${plan.joinKeys.join(\", \")}`);\n }\n if (plan.calculatedMetrics.length > 0) {\n lines.push(`Calculated Metrics: ${plan.calculatedMetrics.join(\", \")}`);\n }\n lines.push(\"\");\n\n if (plan.errors.length > 0) {\n lines.push(\"Errors:\");\n plan.errors.forEach((e) => lines.push(` x ${e}`));\n lines.push(\"\");\n }\n\n if (plan.warnings.length > 0) {\n lines.push(\"Warnings:\");\n plan.warnings.forEach((w) => lines.push(` ! ${w}`));\n lines.push(\"\");\n }\n\n plan.queries.forEach((query, i) => {\n lines.push(`--- Query ${i + 1} ---`);\n lines.push(query.gaql);\n lines.push(`Description: ${query.description}`);\n lines.push(\"\");\n });\n\n return lines.join(\"\\n\");\n}\n\nexport default { planQuery, generateQueryPreview };\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ADS CALCULATED METRICS\n// Client-side metric computation\n// ============================================\n\nimport {\n GoogleAdsInsightRow,\n GoogleAdsDerivedMetrics,\n MICRO_CURRENCY_FACTOR,\n} from \"./types.js\";\n\n// ============================================\n// INDIVIDUAL METRIC CALCULATORS\n// ============================================\n\n/**\n * Calculate ROAS (Return on Ad Spend)\n * Formula: conversions_value / (cost_micros / 1,000,000)\n */\nfunction calcRoas(row: GoogleAdsInsightRow): number | null {\n const costMicros = row[\"metrics.cost_micros\"];\n const value = row[\"metrics.conversions_value\"];\n if (typeof costMicros !== \"number\" || typeof value !== \"number\") return null;\n if (costMicros <= 0) return null;\n const cost = costMicros / MICRO_CURRENCY_FACTOR;\n return value / cost;\n}\n\n/**\n * Calculate All Conversions ROAS\n */\nfunction calcAllConversionsRoas(row: GoogleAdsInsightRow): number | null {\n const costMicros = row[\"metrics.cost_micros\"];\n const value = row[\"metrics.all_conversions_value\"];\n if (typeof costMicros !== \"number\" || typeof value !== \"number\") return null;\n if (costMicros <= 0) return null;\n const cost = costMicros / MICRO_CURRENCY_FACTOR;\n return value / cost;\n}\n\n/**\n * Calculate Value per Conversion\n */\nfunction calcValuePerConversion(row: GoogleAdsInsightRow): number | null {\n const conversions = row[\"metrics.conversions\"];\n const value = row[\"metrics.conversions_value\"];\n if (typeof conversions !== \"number\" || typeof value !== \"number\") return null;\n if (conversions <= 0) return null;\n return value / conversions;\n}\n\n/**\n * Calculate All Conversions Value per Conversion\n */\nfunction calcAllConversionsValuePerConversion(row: GoogleAdsInsightRow): number | null {\n const conversions = row[\"metrics.all_conversions\"];\n const value = row[\"metrics.all_conversions_value\"];\n if (typeof conversions !== \"number\" || typeof value !== \"number\") return null;\n if (conversions <= 0) return null;\n return value / conversions;\n}\n\n/**\n * Calculate CPC in dollars (from micros)\n */\nfunction calcCpcDollars(row: GoogleAdsInsightRow): number | null {\n const costMicros = row[\"metrics.cost_micros\"];\n const clicks = row[\"metrics.clicks\"];\n if (typeof costMicros !== \"number\" || typeof clicks !== \"number\") return null;\n if (clicks <= 0) return null;\n return costMicros / MICRO_CURRENCY_FACTOR / clicks;\n}\n\n/**\n * Calculate CPM in dollars (from micros)\n */\nfunction calcCpmDollars(row: GoogleAdsInsightRow): number | null {\n const costMicros = row[\"metrics.cost_micros\"];\n const impressions = row[\"metrics.impressions\"];\n if (typeof costMicros !== \"number\" || typeof impressions !== \"number\") return null;\n if (impressions <= 0) return null;\n return (costMicros / MICRO_CURRENCY_FACTOR / impressions) * 1000;\n}\n\n/**\n * Calculate conversion rate\n * Formula: conversions / clicks * 100\n */\nfunction calcConversionRate(row: GoogleAdsInsightRow): number | null {\n const conversions = row[\"metrics.conversions\"];\n const clicks = row[\"metrics.clicks\"];\n if (typeof conversions !== \"number\" || typeof clicks !== \"number\") return null;\n if (clicks <= 0) return null;\n return (conversions / clicks) * 100;\n}\n\n/**\n * Calculate Hook Rate (video p25 as proxy for initial attention)\n * For Google Ads: video_quartile_p25_rate is already a rate (0-1)\n */\nfunction calcHookRate(row: GoogleAdsInsightRow): number | null {\n const p25 = row[\"metrics.video_quartile_p25_rate\"];\n if (typeof p25 !== \"number\") return null;\n return p25 * 100;\n}\n\n/**\n * Calculate Hold Rate\n * Formula: video_quartile_p50_rate / video_quartile_p25_rate * 100\n */\nfunction calcHoldRate(row: GoogleAdsInsightRow): number | null {\n const p25 = row[\"metrics.video_quartile_p25_rate\"];\n const p50 = row[\"metrics.video_quartile_p50_rate\"];\n if (typeof p25 !== \"number\" || typeof p50 !== \"number\") return null;\n if (p25 <= 0) return null;\n return (p50 / p25) * 100;\n}\n\n/**\n * Calculate Completion Rate\n * Formula: video_quartile_p100_rate / video_quartile_p25_rate * 100\n */\nfunction calcCompletionRate(row: GoogleAdsInsightRow): number | null {\n const p25 = row[\"metrics.video_quartile_p25_rate\"];\n const p100 = row[\"metrics.video_quartile_p100_rate\"];\n if (typeof p25 !== \"number\" || typeof p100 !== \"number\") return null;\n if (p25 <= 0) return null;\n return (p100 / p25) * 100;\n}\n\n/**\n * Calculate Total Impression Share Lost\n * Formula: search_budget_lost_IS + search_rank_lost_IS\n */\nfunction calcImpressionShareLostTotal(row: GoogleAdsInsightRow): number | null {\n const budgetLost = row[\"metrics.search_budget_lost_impression_share\"];\n const rankLost = row[\"metrics.search_rank_lost_impression_share\"];\n if (typeof budgetLost !== \"number\" && typeof rankLost !== \"number\") return null;\n return ((budgetLost as number) || 0) + ((rankLost as number) || 0);\n}\n\n// ============================================\n// MAIN CALCULATION FUNCTION\n// ============================================\n\n/**\n * Calculate all custom metrics for a single row\n */\nexport function calculateMetrics(\n row: GoogleAdsInsightRow\n): Partial<GoogleAdsDerivedMetrics> {\n return {\n roas: calcRoas(row),\n allConversionsRoas: calcAllConversionsRoas(row),\n valuePerConversion: calcValuePerConversion(row),\n allConversionsValuePerConversion: calcAllConversionsValuePerConversion(row),\n costPerClickDollars: calcCpcDollars(row),\n costPerMilleDollars: calcCpmDollars(row),\n conversionRate: calcConversionRate(row),\n hookRate: calcHookRate(row),\n holdRate: calcHoldRate(row),\n completionRate: calcCompletionRate(row),\n impressionShareLostTotal: calcImpressionShareLostTotal(row),\n };\n}\n\n/**\n * Calculate spend share across all rows (requires full dataset)\n */\nexport function calculateSpendShare(\n rows: GoogleAdsInsightRow[]\n): GoogleAdsInsightRow[] {\n const totalCostMicros = rows.reduce((sum, row) => {\n const cost = row[\"metrics.cost_micros\"];\n return sum + (typeof cost === \"number\" ? cost : 0);\n }, 0);\n\n if (totalCostMicros <= 0) return rows;\n\n return rows.map((row) => {\n const cost = row[\"metrics.cost_micros\"];\n const share =\n typeof cost === \"number\" ? (cost / totalCostMicros) * 100 : null;\n return { ...row, spendShare: share };\n });\n}\n\n/**\n * Enrich rows with all calculated metrics\n */\nexport function enrichWithCalculatedMetrics(\n rows: GoogleAdsInsightRow[],\n requestedCalculated: string[],\n includeSpendShare: boolean = false\n): (GoogleAdsInsightRow & Partial<GoogleAdsDerivedMetrics>)[] {\n let enrichedRows = rows.map((row) => ({\n ...row,\n ...calculateMetrics(row),\n }));\n\n if (includeSpendShare || requestedCalculated.includes(\"spendShare\")) {\n enrichedRows = calculateSpendShare(enrichedRows) as typeof enrichedRows;\n }\n\n return enrichedRows;\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };\nlet currentLevel: LogLevel = (process.env[\"LOG_LEVEL\"] as LogLevel) ?? \"info\";\n\nfunction log(level: LogLevel, platform: string | null, message: string, data?: unknown): void {\n if (LEVEL_ORDER[level] < LEVEL_ORDER[currentLevel]) return;\n console.error(JSON.stringify({\n ts: new Date().toISOString(), level, ...(platform && { platform }), msg: message,\n ...(data !== undefined && { data }),\n }));\n}\n\nexport const logger = {\n debug: (p: string, m: string, d?: unknown) => log(\"debug\", p, m, d),\n info: (p: string, m: string, d?: unknown) => log(\"info\", p, m, d),\n warn: (p: string, m: string, d?: unknown) => log(\"warn\", p, m, d),\n error: (p: string, m: string, d?: unknown) => log(\"error\", p, m, d),\n system: (m: string, d?: unknown) => log(\"info\", null, m, d),\n setLevel: (l: LogLevel) => { currentLevel = l; },\n};\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nexport class PlatformApiError extends Error {\n constructor(\n public readonly platform: string, public readonly code: number, message: string,\n public readonly isRateLimit: boolean = false, public readonly isAuth: boolean = false,\n public readonly isPermission: boolean = false, public readonly suggestion: string = \"\",\n public readonly retryAfter?: number,\n ) { super(message); this.name = \"PlatformApiError\"; }\n\n toMcpError() {\n return { error: this.message, platform: this.platform, code: this.code,\n isRateLimit: this.isRateLimit, isAuth: this.isAuth, suggestion: this.suggestion,\n ...(this.retryAfter !== undefined && { retryAfter: this.retryAfter }) };\n }\n}\n\nexport class RateLimitError extends PlatformApiError {\n constructor(retryAfter?: number) {\n super(\"google-ads\", 429, \"Rate limit exceeded\", true, false, false,\n retryAfter ? `Wait ${retryAfter}s` : \"Wait and retry with backoff\", retryAfter);\n }\n}\n\nexport class AuthError extends PlatformApiError {\n constructor(message?: string) {\n super(\"google-ads\", 401, message ?? \"Auth failed. Check credentials.\",\n false, true, false, \"Verify GOOGLE_ADS_DEVELOPER_TOKEN, CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN\");\n }\n}\n\nexport function formatMcpToolError(error: unknown): { content: Array<{ type: \"text\"; text: string }>; isError: true } {\n if (error instanceof PlatformApiError)\n return { content: [{ type: \"text\", text: JSON.stringify(error.toMcpError(), null, 2) }], isError: true };\n\n // Preserve actionable Google Ads REST details without coupling core errors to\n // the platform-specific exception class (and without returning credentials).\n if (typeof error === \"object\" && error !== null) {\n const record = error as Record<string, unknown>;\n const code = typeof record[\"code\"] === \"number\" ? record[\"code\"] : undefined;\n const status = typeof record[\"status\"] === \"string\" ? record[\"status\"] : undefined;\n if (code !== undefined || status !== undefined) {\n const message = error instanceof Error ? error.message : String(record[\"message\"] ?? \"Google Ads API request failed\");\n const requestId = typeof record[\"requestId\"] === \"string\" ? record[\"requestId\"] : undefined;\n const suggestion = typeof record[\"suggestion\"] === \"string\" ? record[\"suggestion\"] : undefined;\n const details = Array.isArray(record[\"errors\"])\n ? record[\"errors\"].map((detail) => {\n if (typeof detail !== \"object\" || detail === null) return detail;\n const item = detail as Record<string, unknown>;\n return { errorCode: item[\"errorCode\"], message: item[\"message\"] };\n })\n : undefined;\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n error: message,\n platform: \"google-ads\",\n code,\n status,\n requestId,\n isRateLimit: code === 429 || status === \"RESOURCE_EXHAUSTED\",\n isAuth: code === 401 || code === 403,\n suggestion,\n details,\n }, null, 2) }],\n isError: true,\n };\n }\n }\n\n const msg = error instanceof Error ? error.message : String(error);\n return { content: [{ type: \"text\", text: JSON.stringify({ error: msg }, null, 2) }], isError: true };\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { RateLimitError } from \"./errors.js\";\nimport { logger } from \"./logger.js\";\n\nexport class RateLimiter {\n private timestamps: number[] = [];\n private maxPerSecond = 5;\n private maxPerMinute = 300;\n private maxRetries = 3;\n\n async acquire(): Promise<void> {\n const now = Date.now();\n this.timestamps = this.timestamps.filter(t => now - t < 60_000);\n const lastSec = this.timestamps.filter(t => now - t < 1000);\n if (lastSec.length >= this.maxPerSecond) {\n const wait = 1000 - (now - lastSec[0]!) + 50;\n logger.debug(\"google-ads\", `Rate limit: waiting ${wait}ms`);\n await new Promise(r => setTimeout(r, wait));\n }\n if (this.timestamps.length >= this.maxPerMinute) {\n const wait = 60_000 - (now - this.timestamps[0]!) + 100;\n logger.warn(\"google-ads\", `Rate limit: waiting ${wait}ms (per-minute)`);\n await new Promise(r => setTimeout(r, wait));\n }\n this.timestamps.push(Date.now());\n }\n\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n for (let i = 0; i <= this.maxRetries; i++) {\n await this.acquire();\n try { return await fn(); }\n catch (e) {\n const errorRecord = typeof e === \"object\" && e !== null\n ? e as Record<string, unknown>\n : {};\n const isRL = e instanceof RateLimitError\n || errorRecord[\"code\"] === 429\n || errorRecord[\"status\"] === \"RESOURCE_EXHAUSTED\"\n || (e instanceof Error && e.message.toLowerCase().includes(\"rate limit\"));\n if (isRL && i < this.maxRetries) {\n const backoff = Math.min(1000 * Math.pow(2, i) + Math.floor(Math.random() * 500), 30_000);\n logger.warn(\"google-ads\", `Rate limited, retry ${i+1}/${this.maxRetries} in ${backoff}ms`);\n await new Promise(r => setTimeout(r, backoff));\n continue;\n }\n throw e;\n }\n }\n throw new RateLimitError();\n }\n}\n\n/**\n * Keyword Planning methods have a stricter one-request-per-second quota per\n * customer ID. This keyed queue also serializes concurrent MCP tool calls for\n * the same customer while allowing different customers to proceed separately.\n */\nexport class KeywordPlannerRateLimiter {\n private readonly minIntervalMs: number;\n private readonly lastStartedAt = new Map<string, number>();\n private readonly tails = new Map<string, Promise<void>>();\n\n constructor(minIntervalMs = 1_050) {\n this.minIntervalMs = minIntervalMs;\n }\n\n async acquire(customerId: string): Promise<void> {\n const previous = this.tails.get(customerId) ?? Promise.resolve();\n const current = previous\n .catch(() => undefined)\n .then(async () => {\n const lastStartedAt = this.lastStartedAt.get(customerId) ?? 0;\n const waitMs = Math.max(0, this.minIntervalMs - (Date.now() - lastStartedAt));\n if (waitMs > 0) {\n logger.debug(\"google-ads\", `Keyword Planner quota: waiting ${waitMs}ms for customer ${customerId}`);\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n this.lastStartedAt.set(customerId, Date.now());\n });\n\n this.tails.set(customerId, current);\n try {\n await current;\n } finally {\n if (this.tails.get(customerId) === current) {\n this.tails.delete(customerId);\n }\n }\n }\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { formatMcpToolError } from \"../../core/errors.js\";\nimport type { GoogleAdsClient } from \"./client.js\";\n\ntype ToolSuccessFormatter = (data: unknown) => {\n content: Array<{ type: \"text\"; text: string }>;\n};\n\nconst READ_ONLY_OPERATION_NAMES = [\n \"generateAudienceCompositionInsights\",\n \"generateAudienceDefinition\",\n \"generateAudienceOverlapInsights\",\n \"generateBenchmarksMetrics\",\n \"generateCreatorInsights\",\n \"generateReachForecast\",\n \"generateShareablePreviews\",\n \"generateSuggestedTargetingInsights\",\n \"generateTargetingSuggestionMetrics\",\n \"generateTrendingInsights\",\n \"searchAudienceInsightsAttributes\",\n \"suggestBrands\",\n \"suggestKeywordThemes\",\n \"suggestSmartCampaignAd\",\n \"suggestSmartCampaignBudgetOptions\",\n \"suggestTravelAssets\",\n \"listInsightsEligibleDates\",\n \"suggestKeywordThemeConstants\",\n \"generateConversionRates\",\n \"listBenchmarksAvailableDates\",\n \"listBenchmarksLocations\",\n \"listBenchmarksProducts\",\n \"listBenchmarksSources\",\n \"listPlannableLocations\",\n \"listPlannableProducts\",\n \"listPlannableUserInterests\",\n \"listPlannableUserLists\",\n \"getIdentityVerification\",\n \"listInvoices\",\n \"listPaymentsAccounts\",\n] as const;\n\nexport type GoogleAdsReadOnlyOperation = typeof READ_ONLY_OPERATION_NAMES[number];\n\ninterface OperationConfig {\n scope: \"customer\" | \"global\";\n method: \"GET\" | \"POST\";\n path: string;\n purpose: string;\n}\n\nconst CREDENTIAL_REQUEST_KEYS = new Set([\n \"accesstoken\", \"refreshtoken\", \"developertoken\", \"authorization\",\n \"clientsecret\", \"password\", \"apikey\", \"oauthtoken\", \"idtoken\", \"bearertoken\",\n]);\n\nexport const GOOGLE_ADS_READ_ONLY_OPERATIONS: Record<GoogleAdsReadOnlyOperation, OperationConfig> = {\n generateAudienceCompositionInsights: { scope: \"customer\", method: \"POST\", path: \":generateAudienceCompositionInsights\", purpose: \"Audience composition and index insights\" },\n generateAudienceDefinition: { scope: \"customer\", method: \"POST\", path: \":generateAudienceDefinition\", purpose: \"Resolve an audience description into an audience definition\" },\n generateAudienceOverlapInsights: { scope: \"customer\", method: \"POST\", path: \":generateAudienceOverlapInsights\", purpose: \"Audience overlap insights\" },\n generateBenchmarksMetrics: { scope: \"customer\", method: \"POST\", path: \":generateBenchmarksMetrics\", purpose: \"Industry benchmark metrics\" },\n generateCreatorInsights: { scope: \"customer\", method: \"POST\", path: \":generateCreatorInsights\", purpose: \"YouTube creator insights\" },\n generateReachForecast: { scope: \"customer\", method: \"POST\", path: \":generateReachForecast\", purpose: \"Reach Planner forecast\" },\n generateShareablePreviews: { scope: \"customer\", method: \"POST\", path: \":generateShareablePreviews\", purpose: \"Generate shareable ad previews without mutating ads\" },\n generateSuggestedTargetingInsights: { scope: \"customer\", method: \"POST\", path: \":generateSuggestedTargetingInsights\", purpose: \"Suggested targeting insights\" },\n generateTargetingSuggestionMetrics: { scope: \"customer\", method: \"POST\", path: \":generateTargetingSuggestionMetrics\", purpose: \"Targeting suggestion reach metrics\" },\n generateTrendingInsights: { scope: \"customer\", method: \"POST\", path: \":generateTrendingInsights\", purpose: \"Trending search/audience insights\" },\n searchAudienceInsightsAttributes: { scope: \"customer\", method: \"POST\", path: \":searchAudienceInsightsAttributes\", purpose: \"Search audience insight attributes\" },\n suggestBrands: { scope: \"customer\", method: \"POST\", path: \":suggestBrands\", purpose: \"Suggest brand entities for audience insights\" },\n suggestKeywordThemes: { scope: \"customer\", method: \"POST\", path: \":suggestKeywordThemes\", purpose: \"Smart Campaign keyword theme suggestions\" },\n suggestSmartCampaignAd: { scope: \"customer\", method: \"POST\", path: \":suggestSmartCampaignAd\", purpose: \"Smart Campaign ad suggestions\" },\n suggestSmartCampaignBudgetOptions: { scope: \"customer\", method: \"POST\", path: \":suggestSmartCampaignBudgetOptions\", purpose: \"Smart Campaign budget options\" },\n suggestTravelAssets: { scope: \"customer\", method: \"POST\", path: \":suggestTravelAssets\", purpose: \"Travel asset suggestions\" },\n listInsightsEligibleDates: { scope: \"global\", method: \"POST\", path: \"audienceInsights:listInsightsEligibleDates\", purpose: \"Eligible dates for audience insights\" },\n suggestKeywordThemeConstants: { scope: \"global\", method: \"POST\", path: \"keywordThemeConstants:suggest\", purpose: \"Keyword theme constant suggestions\" },\n generateConversionRates: { scope: \"global\", method: \"POST\", path: \":generateConversionRates\", purpose: \"Reach Planner conversion-rate generation\" },\n listBenchmarksAvailableDates: { scope: \"global\", method: \"POST\", path: \":listBenchmarksAvailableDates\", purpose: \"Available benchmark dates\" },\n listBenchmarksLocations: { scope: \"global\", method: \"POST\", path: \":listBenchmarksLocations\", purpose: \"Benchmark locations\" },\n listBenchmarksProducts: { scope: \"global\", method: \"POST\", path: \":listBenchmarksProducts\", purpose: \"Benchmark products\" },\n listBenchmarksSources: { scope: \"global\", method: \"POST\", path: \":listBenchmarksSources\", purpose: \"Benchmark sources\" },\n listPlannableLocations: { scope: \"global\", method: \"POST\", path: \":listPlannableLocations\", purpose: \"Reach Planner locations\" },\n listPlannableProducts: { scope: \"global\", method: \"POST\", path: \":listPlannableProducts\", purpose: \"Reach Planner products\" },\n listPlannableUserInterests: { scope: \"global\", method: \"POST\", path: \":listPlannableUserInterests\", purpose: \"Reach Planner user interests\" },\n listPlannableUserLists: { scope: \"global\", method: \"POST\", path: \":listPlannableUserLists\", purpose: \"Reach Planner user lists\" },\n getIdentityVerification: { scope: \"customer\", method: \"GET\", path: \"/getIdentityVerification\", purpose: \"Advertiser identity-verification status\" },\n listInvoices: { scope: \"customer\", method: \"GET\", path: \"/invoices\", purpose: \"Billing invoices\" },\n listPaymentsAccounts: { scope: \"customer\", method: \"GET\", path: \"/paymentsAccounts\", purpose: \"Payments account metadata\" },\n};\n\nexport function isGoogleAdsReadOnlyServicePath(\n path: string,\n method: \"GET\" | \"POST\"\n): boolean {\n const customerSuffix = /^customers\\/\\d+([:/].+)$/.exec(path)?.[1];\n return Object.values(GOOGLE_ADS_READ_ONLY_OPERATIONS).some((config) =>\n config.method === method\n && (config.scope === \"customer\" ? customerSuffix === config.path : path === config.path)\n );\n}\n\nexport function validateGoogleAdsReadOnlyRequest(\n value: unknown,\n path = \"request\",\n depth = 0,\n state = { nodes: 0 }\n): void {\n state.nodes += 1;\n if (state.nodes > 5_000) throw new Error(\"Request contains too many JSON values.\");\n if (depth > 10) throw new Error(\"Request nesting is limited to 10 levels.\");\n if (value === null || typeof value === \"boolean\") return;\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) throw new Error(`${path} must contain finite numbers.`);\n return;\n }\n if (typeof value === \"string\") {\n if (value.length > 20_000) throw new Error(`${path} string exceeds 20,000 characters.`);\n return;\n }\n if (Array.isArray(value)) {\n if (value.length > 2_000) throw new Error(`${path} array exceeds 2,000 entries.`);\n value.forEach((item, index) => validateGoogleAdsReadOnlyRequest(item, `${path}[${index}]`, depth + 1, state));\n return;\n }\n if (typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>);\n if (entries.length > 500) throw new Error(`${path} object exceeds 500 fields.`);\n for (const [key, item] of entries) {\n if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(key)) throw new Error(`Invalid request key ${path}.${key}.`);\n const normalizedKey = key.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n if (CREDENTIAL_REQUEST_KEYS.has(normalizedKey)) {\n throw new Error(`Credentials are not accepted in ${path}.${key}.`);\n }\n validateGoogleAdsReadOnlyRequest(item, `${path}.${key}`, depth + 1, state);\n }\n return;\n }\n throw new Error(`${path} must contain JSON-compatible values.`);\n}\n\nexport function buildGoogleAdsReadOnlyPath(\n operation: GoogleAdsReadOnlyOperation,\n customerId?: string\n): { path: string; config: OperationConfig } {\n const config = GOOGLE_ADS_READ_ONLY_OPERATIONS[operation];\n if (!config) throw new Error(`Unsupported read-only operation: ${operation}.`);\n if (config.scope === \"customer\") {\n const cleanCustomerId = customerId?.replace(/-/g, \"\");\n if (!cleanCustomerId || !/^\\d+$/.test(cleanCustomerId)) {\n throw new Error(`${operation} requires a numeric customerId.`);\n }\n return { path: `customers/${cleanCustomerId}${config.path}`, config };\n }\n return { path: config.path, config };\n}\n\nexport function registerGoogleAdsReadOnlyRpcTool(\n server: McpServer,\n client: GoogleAdsClient,\n ok: ToolSuccessFormatter\n): void {\n server.tool(\n \"google_ads_run_readonly_rpc\",\n \"Advanced read-only escape hatch for allowlisted Google Ads services outside GAQL: Audience Insights, Reach Planner, benchmarks, creator/trending insights, targeting suggestions, Smart Campaign suggestions, identity verification, invoices, and payments accounts. The operation is an enum; arbitrary paths and all mutations/uploads are impossible.\",\n {\n operation: z.enum(READ_ONLY_OPERATION_NAMES),\n customerId: z.string().regex(/^\\d[\\d-]*\\d$|^\\d$/).optional().describe(\"Required for customer-scoped operations; omit for global planning catalogs\"),\n request: z.record(z.unknown()).optional().default({}).describe(\"Official REST JSON request body for POST operations, or query parameters for GET operations\"),\n },\n async ({ operation, customerId, request }) => {\n try {\n validateGoogleAdsReadOnlyRequest(request);\n const encodedLength = JSON.stringify(request).length;\n if (encodedLength > 100_000) throw new Error(\"Encoded request exceeds 100,000 characters.\");\n const { path, config } = buildGoogleAdsReadOnlyPath(operation, customerId);\n let response: Record<string, unknown>;\n if (config.method === \"GET\") {\n const query: Record<string, string | number | boolean | undefined> = {};\n for (const [key, value] of Object.entries(request)) {\n if (![\"string\", \"number\", \"boolean\"].includes(typeof value)) {\n throw new Error(`GET query parameter ${key} must be a string, number, or boolean.`);\n }\n query[key] = value as string | number | boolean;\n }\n response = await client.runReadOnlyService(path, \"GET\", undefined, query);\n } else {\n response = await client.runReadOnlyService(path, \"POST\", request);\n }\n return ok({\n dataKind: \"google_ads_readonly_rpc\",\n operation,\n purpose: config.purpose,\n scope: config.scope,\n method: config.method,\n response,\n readOnly: true,\n warnings: operation === \"generateShareablePreviews\"\n ? [\"The returned preview URL is shareable and can expose ad creative until it expires; disclose it only to intended recipients.\"]\n : [],\n limitations: [\n \"The request body follows Google's native REST schema and is intentionally not translated into this server's metric aliases.\",\n \"Availability depends on developer-token access level, account eligibility, OAuth permissions, and operation-specific quotas.\",\n ],\n nextActions: [\"Use google_ads_search_fields and google_ads_run_gaql for queryable resources; use this tool only for non-GAQL services.\"],\n debug: { requestCount: 1 },\n });\n } catch (error) {\n return formatMcpToolError(error);\n }\n }\n );\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ADS API CLIENT (READ-ONLY)\n// Google Ads API client, adapted for MCP server\n// Auto-refreshes OAuth tokens, rate-limited\n// ============================================\n\nimport {\n GoogleAdsCustomer,\n GoogleAdsRow,\n GoogleAdsInsightRow,\n GoogleAdsQueryRequest,\n GoogleAdsQueryDebugInfo,\n GoogleAdsApiException,\n GoogleAdsDerivedMetrics,\n GOOGLE_ADS_API_BASE_URL,\n MICRO_CURRENCY_FACTOR,\n CAMEL_TO_SNAKE_METRIC_MAP,\n CAMEL_TO_SNAKE_SEGMENT_MAP,\n stripCustomerId,\n GenerateKeywordHistoricalMetricsRequest,\n GenerateKeywordHistoricalMetricsResponse,\n GenerateKeywordIdeasRequest,\n GenerateKeywordIdeasResponse,\n GenerateKeywordForecastMetricsRequest,\n GenerateKeywordForecastMetricsResponse,\n GenerateAdGroupThemesRequest,\n GenerateAdGroupThemesResponse,\n SearchGoogleAdsFieldsRequest,\n SearchGoogleAdsFieldsResponse,\n SuggestGeoTargetConstantsRequest,\n SuggestGeoTargetConstantsResponse,\n} from \"./types.js\";\n\n/**\n * OAuth2 token refresh response shape\n */\ninterface GoogleAdsRefreshTokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n scope: string;\n}\n\nexport { GoogleAdsApiException } from \"./types.js\";\nimport { planQuery } from \"./query-planner.js\";\nimport { calculateMetrics } from \"./calculated-metrics.js\";\nimport { logger } from \"../../core/logger.js\";\nimport { KeywordPlannerRateLimiter, RateLimiter } from \"../../core/rate-limiter.js\";\nimport { isGoogleAdsReadOnlyServicePath } from \"./read-rpc.js\";\n\nconst DEFAULT_GOOGLE_ADS_REQUEST_TIMEOUT_MS = 30_000;\n\nfunction googleAdsRequestTimeoutMs(): number {\n const parsed = Number(process.env[\"GOOGLE_ADS_REQUEST_TIMEOUT_MS\"]);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_GOOGLE_ADS_REQUEST_TIMEOUT_MS;\n}\n\n// ============================================\n// CLIENT CONFIG\n// ============================================\n\nexport interface GoogleAdsClientConfig {\n developerToken: string;\n clientId: string;\n clientSecret: string;\n refreshToken: string;\n loginCustomerId?: string;\n}\n\n// ============================================\n// GOOGLE ADS CLIENT CLASS\n// ============================================\n\nexport class GoogleAdsClient {\n private developerToken: string;\n private clientId: string;\n private clientSecret: string;\n private refreshToken: string;\n private loginCustomerId?: string;\n private accessToken: string = \"\";\n private tokenExpiresAt: number = 0;\n private rateLimiter = new RateLimiter();\n private keywordPlannerRateLimiter = new KeywordPlannerRateLimiter();\n\n constructor(config: GoogleAdsClientConfig) {\n this.developerToken = config.developerToken;\n this.clientId = config.clientId;\n this.clientSecret = config.clientSecret;\n this.refreshToken = config.refreshToken;\n this.loginCustomerId = config.loginCustomerId\n ? stripCustomerId(config.loginCustomerId)\n : undefined;\n }\n\n // ============================================\n // TOKEN MANAGEMENT (PRIVATE)\n // ============================================\n\n /**\n * Refresh access token using refresh token.\n * Google Ads refresh tokens never expire.\n */\n private async refreshAccessToken(): Promise<GoogleAdsRefreshTokenResponse> {\n const response = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n redirect: \"error\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: this.refreshToken,\n client_id: this.clientId,\n client_secret: this.clientSecret,\n }),\n });\n\n if (!response.ok) {\n const errBody = await response.json().catch(() => ({})) as Record<string, string>;\n throw new GoogleAdsApiException(\n errBody.error_description || errBody.error || \"Failed to refresh token\",\n response.status,\n errBody.error\n );\n }\n\n return response.json() as Promise<GoogleAdsRefreshTokenResponse>;\n }\n\n /**\n * Ensure we have a valid (non-expired) access token.\n * Called automatically before every API request.\n */\n private async ensureValidToken(): Promise<void> {\n // Refresh if token is missing or will expire within 60 seconds\n if (!this.accessToken || Date.now() >= this.tokenExpiresAt - 60_000) {\n logger.info(\"google-ads\", \"Refreshing access token\");\n const tokenResponse = await this.refreshAccessToken();\n this.accessToken = tokenResponse.access_token;\n // Google tokens typically expire in 3600s; use expires_in if available\n this.tokenExpiresAt = Date.now() + (tokenResponse.expires_in ?? 3600) * 1000;\n logger.info(\"google-ads\", \"Access token refreshed successfully\");\n }\n }\n\n // ============================================\n // PRIVATE METHODS\n // ============================================\n\n private getHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.accessToken}`,\n \"developer-token\": this.developerToken,\n \"Content-Type\": \"application/json\",\n };\n\n if (this.loginCustomerId) {\n headers[\"login-customer-id\"] = this.loginCustomerId;\n }\n\n return headers;\n }\n\n private async request<T>(\n url: string,\n options: RequestInit = {},\n beforeAttempt?: () => Promise<void>,\n deadlineAtMs?: number\n ): Promise<T> {\n await this.ensureValidToken();\n\n return this.rateLimiter.execute<T>(async () => {\n // Some Google Ads services (notably Keyword Planner) impose a stricter\n // per-customer quota. Run this hook for every physical HTTP attempt so\n // OAuth latency, concurrent calls, and automatic retries cannot bunch\n // requests together after an earlier logical-call-level wait.\n await beforeAttempt?.();\n\n // Hard timeout to prevent hanging requests from blocking agent sessions.\n const controller = new AbortController();\n const configuredTimeoutMs = googleAdsRequestTimeoutMs();\n const remainingTimeMs = deadlineAtMs === undefined\n ? configuredTimeoutMs\n : deadlineAtMs - Date.now();\n if (remainingTimeMs <= 0) {\n throw new GoogleAdsApiException(\n \"Google Ads request skipped because the MCP tool time budget was exhausted\",\n 408,\n \"TIME_BUDGET_EXHAUSTED\"\n );\n }\n const timeoutMs = Math.min(configuredTimeoutMs, remainingTimeMs);\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n // Forced after the spread: once a bearer token is attached, a redirect\n // must never be followed, or the credential would be forwarded to\n // whatever host the redirect names.\n redirect: \"error\",\n headers: {\n ...this.getHeaders(),\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const errorBody = await response.json().catch(() => ({})) as any;\n const errorDetails = errorBody?.error;\n\n // Let tool handlers decide whether an API failure is fatal. Some discovery\n // paths intentionally catch permission errors and return structured fallbacks.\n logger.debug(\"google-ads\", `API Error: ${response.status}`, errorBody);\n\n const gaErrors = errorDetails?.details?.[0]?.errors\n ?? errorBody?.[0]?.error?.details?.[0]?.errors;\n const requestId = errorDetails?.details?.[0]?.requestId\n ?? errorBody?.[0]?.error?.details?.[0]?.requestId;\n const detailedMessage = gaErrors?.[0]?.message\n || errorDetails?.message\n || `Request failed: ${response.statusText}`;\n\n throw new GoogleAdsApiException(\n detailedMessage,\n response.status,\n errorDetails?.status,\n requestId,\n gaErrors\n );\n }\n\n return response.json() as Promise<T>;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw new GoogleAdsApiException(\n `Google Ads API request timed out after ${Math.round(timeoutMs / 1000)} seconds`,\n 408,\n \"TIMEOUT\"\n );\n }\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n });\n }\n\n // ============================================\n // CORE QUERY METHODS\n // ============================================\n\n /**\n * Execute a GAQL query using SearchStream (streaming, no pagination)\n * Preferred for reporting: lower latency\n */\n /**\n * Send a mutate request to a Google Ads resource collection.\n *\n * Only the write tools call this, and they are registered only when\n * GOOGLE_ADS_ENABLE_WRITES is set. It reuses the cached access token rather\n * than refreshing OAuth on every call.\n */\n async mutate(\n customerId: string,\n collection: string,\n operations: unknown[],\n loginCustomerId?: string,\n ): Promise<unknown> {\n await this.ensureValidToken();\n const cid = stripCustomerId(customerId);\n const headers = this.getHeaders();\n if (loginCustomerId) headers[\"login-customer-id\"] = stripCustomerId(loginCustomerId);\n\n const response = await fetch(\n `${GOOGLE_ADS_API_BASE_URL}/customers/${cid}/${collection}:mutate`,\n { method: \"POST\", headers, body: JSON.stringify({ operations }), redirect: \"error\" },\n );\n const body = await response.text();\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n parsed = body;\n }\n if (!response.ok) {\n const detail = typeof parsed === \"string\" ? parsed : JSON.stringify(parsed);\n throw new GoogleAdsApiException(detail.slice(0, 400), response.status);\n }\n return parsed;\n }\n\n async searchStream(customerId: string, gaqlQuery: string): Promise<GoogleAdsRow[]> {\n const cleanCustomerId = stripCustomerId(customerId);\n const url = `${GOOGLE_ADS_API_BASE_URL}/customers/${cleanCustomerId}/googleAds:searchStream`;\n\n const response = await this.request<Array<{ results?: GoogleAdsRow[]; fieldMask?: string; requestId?: string }>>(\n url,\n {\n method: \"POST\",\n body: JSON.stringify({ query: gaqlQuery }),\n }\n );\n\n // SearchStream returns an array of batches, each containing results\n const allRows: GoogleAdsRow[] = [];\n for (const batch of response) {\n if (batch.results) {\n allRows.push(...batch.results);\n }\n }\n\n return allRows;\n }\n\n /**\n * Execute a GAQL query using Search.\n * Google Ads v23 uses a fixed response page size; limit rows with GAQL LIMIT.\n */\n async search(\n customerId: string,\n gaqlQuery: string,\n pageToken?: string\n ): Promise<{ results: GoogleAdsRow[]; nextPageToken?: string; totalResultsCount?: string }> {\n const cleanCustomerId = stripCustomerId(customerId);\n const url = `${GOOGLE_ADS_API_BASE_URL}/customers/${cleanCustomerId}/googleAds:search`;\n\n const body: Record<string, unknown> = { query: gaqlQuery };\n if (pageToken) body.pageToken = pageToken;\n\n return this.request(url, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n }\n\n // ============================================\n // KEYWORD PLANNER (PLANLESS, READ-ONLY RPCS)\n // ============================================\n\n /**\n * Return historical Keyword Planner metrics for a supplied keyword list.\n * This POST is a read-only custom method and does not create a saved plan.\n */\n async generateKeywordHistoricalMetrics(\n customerId: string,\n body: GenerateKeywordHistoricalMetricsRequest\n ): Promise<GenerateKeywordHistoricalMetricsResponse> {\n const cleanCustomerId = stripCustomerId(customerId);\n const url = `${GOOGLE_ADS_API_BASE_URL}/customers/${cleanCustomerId}:generateKeywordHistoricalMetrics`;\n return this.request(\n url,\n { method: \"POST\", body: JSON.stringify(body) },\n () => this.keywordPlannerRateLimiter.acquire(cleanCustomerId)\n );\n }\n\n /**\n * Generate keyword ideas and their historical metrics without saving a plan.\n */\n async generateKeywordIdeas(\n customerId: string,\n body: GenerateKeywordIdeasRequest\n ): Promise<GenerateKeywordIdeasResponse> {\n const cleanCustomerId = stripCustomerId(customerId);\n const url = `${GOOGLE_ADS_API_BASE_URL}/customers/${cleanCustomerId}:generateKeywordIdeas`;\n return this.request(\n url,\n { method: \"POST\", body: JSON.stringify(body) },\n () => this.keywordPlannerRateLimiter.acquire(cleanCustomerId)\n );\n }\n\n /**\n * Forecast a temporary keyword campaign without mutating the Google Ads account.\n */\n async generateKeywordForecastMetrics(\n customerId: string,\n body: GenerateKeywordForecastMetricsRequest,\n deadlineAtMs?: number\n ): Promise<GenerateKeywordForecastMetricsResponse> {\n const cleanCustomerId = stripCustomerId(customerId);\n const url = `${GOOGLE_ADS_API_BASE_URL}/customers/${cleanCustomerId}:generateKeywordForecastMetrics`;\n return this.request(\n url,\n { method: \"POST\", body: JSON.stringify(body) },\n () => this.keywordPlannerRateLimiter.acquire(cleanCustomerId),\n deadlineAtMs\n );\n }\n\n /**\n * Search Google's live GAQL field catalog so callers can discover every\n * resource, attribute, segment, metric, enum, and compatibility edge.\n */\n async searchGoogleAdsFields(\n body: SearchGoogleAdsFieldsRequest\n ): Promise<SearchGoogleAdsFieldsResponse> {\n const url = `${GOOGLE_ADS_API_BASE_URL}/googleAdsFields:search`;\n return this.request(url, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n }\n\n /** Resolve human-readable locations or known IDs to targetable geo constants. */\n async suggestGeoTargetConstants(\n body: SuggestGeoTargetConstantsRequest\n ): Promise<SuggestGeoTargetConstantsResponse> {\n const url = `${GOOGLE_ADS_API_BASE_URL}/geoTargetConstants:suggest`;\n return this.request(url, {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n }\n\n /**\n * Organize keyword ideas into existing ad groups without mutating them.\n * This belongs to KeywordPlanIdeaService and shares its strict quota.\n */\n async generateAdGroupThemes(\n customerId: string,\n body: GenerateAdGroupThemesRequest\n ): Promise<GenerateAdGroupThemesResponse> {\n const cleanCustomerId = stripCustomerId(customerId);\n const url = `${GOOGLE_ADS_API_BASE_URL}/customers/${cleanCustomerId}:generateAdGroupThemes`;\n return this.request(\n url,\n { method: \"POST\", body: JSON.stringify(body) },\n () => this.keywordPlannerRateLimiter.acquire(cleanCustomerId)\n );\n }\n\n /**\n * Execute an explicitly allowlisted read/generate/suggest/list service path.\n * The client rechecks the shared operation catalog plus path, method, and\n * mutation-word defenses before attaching credentials.\n */\n async runReadOnlyService<T = Record<string, unknown>>(\n relativePath: string,\n method: \"GET\" | \"POST\",\n body?: Record<string, unknown>,\n query?: Record<string, string | number | boolean | undefined>\n ): Promise<T> {\n if (method !== \"GET\" && method !== \"POST\") {\n throw new Error(\"Google Ads read-only services support only GET or POST.\");\n }\n const path = relativePath.replace(/^\\/+|\\/+$/g, \"\");\n if (!path || path.length > 300 || path.includes(\"..\") || /[?#]/.test(path)) {\n throw new Error(\"Invalid Google Ads read-only service path.\");\n }\n if (!/^[A-Za-z0-9_:/.-]+$/.test(path)) {\n throw new Error(\"Google Ads read-only service path contains unsupported characters.\");\n }\n if (/\\b(?:mutate|create|update|delete|remove|upload|apply|dismiss|start|book|resolve|cancel|run)\\b/i.test(path.replace(/([a-z])([A-Z])/g, \"$1 $2\"))) {\n throw new Error(\"Mutation-like Google Ads service paths are blocked.\");\n }\n if (!isGoogleAdsReadOnlyServicePath(path, method)) {\n throw new Error(\"Google Ads service path is not in the read-only service allowlist.\");\n }\n if (method === \"GET\" && !/\\/(?:getIdentityVerification|invoices|paymentsAccounts)$/.test(path)) {\n throw new Error(\"GET is only allowed for identity verification, invoices, and payments accounts.\");\n }\n if (method === \"POST\" && /\\/(?:getIdentityVerification|invoices|paymentsAccounts)$/.test(path)) {\n throw new Error(\"This Google Ads read endpoint requires GET.\");\n }\n\n const url = new URL(path.startsWith(\":\")\n ? `${GOOGLE_ADS_API_BASE_URL}${path}`\n : `${GOOGLE_ADS_API_BASE_URL}/${path}`);\n for (const [key, value] of Object.entries(query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n return this.request<T>(url.toString(), {\n method,\n redirect: \"error\",\n ...(method === \"POST\" ? { body: JSON.stringify(body ?? {}) } : {}),\n });\n }\n\n // ============================================\n // ACCOUNT MANAGEMENT\n // ============================================\n\n /**\n * List all accessible customer IDs (for MCC accounts)\n * Returns resource names like \"customers/1234567890\"\n */\n async listAccessibleCustomers(): Promise<string[]> {\n const url = `${GOOGLE_ADS_API_BASE_URL}/customers:listAccessibleCustomers`;\n\n const response = await this.request<{ resourceNames: string[] }>(url);\n return response.resourceNames;\n }\n\n /**\n * Get customer details by ID\n */\n async getCustomer(customerId: string): Promise<GoogleAdsCustomer> {\n const cleanCustomerId = stripCustomerId(customerId);\n const gaql = `\n SELECT\n customer.id,\n customer.descriptive_name,\n customer.currency_code,\n customer.time_zone,\n customer.manager,\n customer.test_account\n FROM customer\n LIMIT 1\n `;\n\n const rows = await this.searchStream(cleanCustomerId, gaql);\n if (rows.length === 0) {\n throw new GoogleAdsApiException(\n `Customer ${customerId} not found`,\n 404,\n \"NOT_FOUND\"\n );\n }\n\n const row = rows[0];\n return {\n id: String(row.customer?.id || cleanCustomerId),\n descriptiveName: row.customer?.descriptiveName || \"Unknown Account\",\n currencyCode: row.customer?.currencyCode || \"USD\",\n timeZone: row.customer?.timeZone || \"America/New_York\",\n manager: row.customer?.manager || false,\n testAccount: row.customer?.testAccount || false,\n resourceName: row.customer?.resourceName || `customers/${cleanCustomerId}`,\n };\n }\n\n /**\n * Get all accessible customer accounts with their details\n */\n async getAllCustomers(): Promise<GoogleAdsCustomer[]> {\n const resourceNames = await this.listAccessibleCustomers();\n const customers: GoogleAdsCustomer[] = [];\n\n for (const resourceName of resourceNames) {\n const customerId = resourceName.replace(\"customers/\", \"\");\n try {\n const customer = await this.getCustomer(customerId);\n customers.push(customer);\n } catch (error) {\n // Skip accounts we can't access (e.g., suspended accounts)\n logger.warn(\"google-ads\", `Skipping customer ${customerId}: ${error instanceof Error ? error.message : error}`);\n }\n }\n\n return customers;\n }\n\n /**\n * Get client accounts under a Manager (MCC) account\n * Uses customer_client resource to list sub-accounts\n */\n async getClientAccounts(mccId: string): Promise<GoogleAdsCustomer[]> {\n const cleanMccId = stripCustomerId(mccId);\n const gaql = `\n SELECT\n customer_client.id,\n customer_client.descriptive_name,\n customer_client.currency_code,\n customer_client.time_zone,\n customer_client.manager,\n customer_client.test_account,\n customer_client.level,\n customer_client.status\n FROM customer_client\n WHERE customer_client.level <= 1\n AND customer_client.status = 'ENABLED'\n `;\n\n const rows = await this.searchStream(cleanMccId, gaql);\n const clients: GoogleAdsCustomer[] = [];\n\n for (const row of rows) {\n const cc = row.customerClient as Record<string, unknown> | undefined;\n if (!cc) continue;\n\n const id = String(cc.id || \"\");\n // Skip the MCC itself (level 0)\n if (id === cleanMccId) continue;\n\n clients.push({\n id,\n descriptiveName: (cc.descriptiveName as string) || \"Unnamed Account\",\n currencyCode: (cc.currencyCode as string) || \"USD\",\n timeZone: (cc.timeZone as string) || \"America/New_York\",\n manager: (cc.manager as boolean) || false,\n testAccount: (cc.testAccount as boolean) || false,\n resourceName: `customers/${id}`,\n });\n }\n\n return clients;\n }\n\n // ============================================\n // HIGH-LEVEL QUERY EXECUTION\n // ============================================\n\n /**\n * Execute a query request with automatic query planning\n */\n async executeQuery(\n request: GoogleAdsQueryRequest\n ): Promise<{\n data: (GoogleAdsInsightRow & Partial<GoogleAdsDerivedMetrics>)[];\n debug: GoogleAdsQueryDebugInfo;\n }> {\n const startTime = Date.now();\n const plan = planQuery(request);\n\n const errors: string[] = [...plan.errors];\n const warnings: string[] = [...plan.warnings];\n const gaqlQueries: string[] = [];\n\n // Check if plan has errors\n if (plan.errors.length > 0) {\n return {\n data: [],\n debug: {\n requestCount: 0,\n totalRows: 0,\n executionTimeMs: Date.now() - startTime,\n errors,\n warnings,\n rawRequests: [],\n rawResponses: [],\n calculatedMetrics: plan.calculatedMetrics,\n joinKeys: plan.joinKeys,\n gaqlQueries: [],\n },\n };\n }\n\n const rawRequests: unknown[] = [];\n const rawResponses: unknown[] = [];\n const queryResults: GoogleAdsInsightRow[][] = [];\n\n // Execute each GAQL query in the plan\n for (const query of plan.queries) {\n gaqlQueries.push(query.gaql);\n rawRequests.push({ gaql: query.gaql, resource: query.resource });\n\n try {\n const apiRows = (await this.search(request.customerId, query.gaql)).results ?? [];\n rawResponses.push(apiRows.slice(0, 5)); // Only store first 5 for debug\n\n // Flatten nested rows\n const flatRows = apiRows.map((row) => flattenGoogleAdsRow(row));\n queryResults.push(flatRows);\n } catch (error) {\n rawResponses.push({\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n errors.push(\n error instanceof Error ? error.message : \"Query execution failed\"\n );\n queryResults.push([]);\n }\n }\n\n // Merge results based on merge strategy\n let allRows: GoogleAdsInsightRow[];\n\n if (plan.mergeStrategy === \"join\" && queryResults.length > 1 && plan.joinKeys.length > 0) {\n // JOIN: Use first query as base, merge subsequent queries by join keys\n allRows = [...(queryResults[0] || [])];\n\n for (let qi = 1; qi < queryResults.length; qi++) {\n const supplementaryRows = queryResults[qi];\n if (supplementaryRows.length === 0) continue;\n\n // Build lookup map from supplementary rows keyed by join key composite\n const lookup = new Map<string, GoogleAdsInsightRow>();\n for (const row of supplementaryRows) {\n const compositeKey = plan.joinKeys\n .map((jk) => String(row[jk] ?? \"\"))\n .join(\"|||\");\n lookup.set(compositeKey, row);\n }\n\n // Merge supplementary metrics into base rows\n for (const baseRow of allRows) {\n const compositeKey = plan.joinKeys\n .map((jk) => String(baseRow[jk] ?? \"\"))\n .join(\"|||\");\n const match = lookup.get(compositeKey);\n if (match) {\n // Merge all fields from the supplementary row that don't exist in base\n for (const [key, value] of Object.entries(match)) {\n if (!(key in baseRow)) {\n (baseRow as Record<string, unknown>)[key] = value;\n }\n }\n }\n }\n }\n } else if (queryResults.length === 1) {\n allRows = queryResults[0] || [];\n } else {\n // UNION or fallback: concatenate all rows\n allRows = queryResults.flat();\n }\n\n // Calculate only the derived metrics the caller actually requested\n const requestedCalc = new Set(plan.calculatedMetrics);\n const enrichedRows = requestedCalc.size > 0\n ? allRows.map((row) => {\n const all = calculateMetrics(row);\n const picked: Record<string, unknown> = {};\n for (const key of requestedCalc) {\n if (key in all && (all as Record<string, unknown>)[key] !== null) {\n picked[key] = (all as Record<string, unknown>)[key];\n }\n }\n return { ...row, ...picked };\n })\n : allRows;\n\n return {\n data: enrichedRows,\n debug: {\n requestCount: plan.queries.length,\n totalRows: enrichedRows.length,\n executionTimeMs: Date.now() - startTime,\n errors,\n warnings,\n rawRequests,\n rawResponses,\n calculatedMetrics: plan.calculatedMetrics,\n joinKeys: plan.joinKeys,\n gaqlQueries,\n },\n };\n }\n}\n\n// ============================================\n// ROW FLATTENING\n// ============================================\n\nconst KNOWN_MICRO_FIELDS = new Set([\n \"campaign_budget.amount_micros\",\n \"campaign_budget.total_amount_micros\",\n \"campaign.target_cpa.target_cpa_micros\",\n \"campaign.maximize_conversions.target_cpa_micros\",\n \"campaign.target_impression_share.cpc_bid_ceiling_micros\",\n \"campaign.target_impression_share.location_fraction_micros\",\n]);\n\nfunction convertValueIfNumeric(flatKey: string, value: unknown): unknown {\n if (typeof value === \"string\" && /^-?\\d+(\\.\\d+)?$/.test(value)) {\n const num = parseFloat(value);\n if (KNOWN_MICRO_FIELDS.has(flatKey)) {\n return num / MICRO_CURRENCY_FACTOR;\n }\n return num;\n }\n return value;\n}\n\n/**\n * Flatten a nested GoogleAdsRow into a flat GoogleAdsInsightRow\n * Converts camelCase API fields to snake_case GAQL-style keys\n *\n * Input: { campaign: { id: \"123\", name: \"My Campaign\" }, metrics: { impressions: \"5000\", costMicros: \"25000000\" } }\n * Output: { \"campaign.id\": \"123\", \"campaign.name\": \"My Campaign\", \"metrics.impressions\": 5000, \"metrics.cost_micros\": 25000000, \"metrics.cost\": 25.0 }\n */\nexport function flattenGoogleAdsRow(row: GoogleAdsRow): GoogleAdsInsightRow {\n const flat: GoogleAdsInsightRow = {};\n\n // Flatten each top-level key\n for (const [topKey, topValue] of Object.entries(row)) {\n if (topValue === null || topValue === undefined) continue;\n if (typeof topValue !== \"object\") continue;\n\n const obj = topValue as Record<string, unknown>;\n\n // Map top-level camelCase to GAQL resource names\n const resourceName = camelToSnakeResource(topKey);\n\n for (const [fieldKey, fieldValue] of Object.entries(obj)) {\n if (fieldValue === null || fieldValue === undefined) continue;\n\n // Convert field name from camelCase to snake_case\n let snakeField: string;\n if (topKey === \"metrics\" && CAMEL_TO_SNAKE_METRIC_MAP[fieldKey]) {\n snakeField = CAMEL_TO_SNAKE_METRIC_MAP[fieldKey];\n } else if (topKey === \"segments\" && CAMEL_TO_SNAKE_SEGMENT_MAP[fieldKey]) {\n snakeField = CAMEL_TO_SNAKE_SEGMENT_MAP[fieldKey];\n } else {\n snakeField = camelToSnake(fieldKey);\n }\n\n const flatKey = `${resourceName}.${snakeField}`;\n\n // Convert string numbers to actual numbers for metrics\n if (topKey === \"metrics\") {\n const numValue =\n typeof fieldValue === \"string\" ? parseFloat(fieldValue) : fieldValue;\n flat[flatKey] = numValue;\n\n // Auto-convert micro currency fields\n if (snakeField === \"cost_micros\" && typeof numValue === \"number\") {\n flat[\"metrics.cost\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"average_cpc\" && typeof numValue === \"number\") {\n flat[\"metrics.average_cpc\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"average_cpm\" && typeof numValue === \"number\") {\n flat[\"metrics.average_cpm\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"cost_per_conversion\" && typeof numValue === \"number\") {\n flat[\"metrics.cost_per_conversion\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"cost_per_all_conversions\" && typeof numValue === \"number\") {\n flat[\"metrics.cost_per_all_conversions\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"active_view_cpm\" && typeof numValue === \"number\") {\n flat[\"metrics.active_view_cpm\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"average_cost\" && typeof numValue === \"number\") {\n flat[\"metrics.average_cost\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"average_cpe\" && typeof numValue === \"number\") {\n flat[\"metrics.average_cpe\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"average_cpv\" && typeof numValue === \"number\") {\n flat[\"metrics.average_cpv\"] = numValue / MICRO_CURRENCY_FACTOR;\n } else if (snakeField === \"trueview_average_cpv\" && typeof numValue === \"number\") {\n flat[\"metrics.trueview_average_cpv\"] = numValue / MICRO_CURRENCY_FACTOR;\n }\n } else if (typeof fieldValue === \"object\" && !Array.isArray(fieldValue)) {\n // Handle nested objects (e.g., adGroupAd.ad, adGroupCriterion.keyword)\n const nested = fieldValue as Record<string, unknown>;\n for (const [nestedKey, nestedValue] of Object.entries(nested)) {\n if (nestedValue === null || nestedValue === undefined) continue;\n if (typeof nestedValue === \"object\" && !Array.isArray(nestedValue)) {\n // One more level deep (e.g., keyword.info.text)\n const deepNested = nestedValue as Record<string, unknown>;\n for (const [deepKey, deepValue] of Object.entries(deepNested)) {\n if (deepValue === null || deepValue === undefined) continue;\n const deepFlatKey = `${resourceName}.${snakeField}.${camelToSnake(nestedKey)}.${camelToSnake(deepKey)}`;\n flat[deepFlatKey] = convertValueIfNumeric(deepFlatKey, deepValue);\n }\n } else {\n const nestedFlatKey = `${resourceName}.${snakeField}.${camelToSnake(nestedKey)}`;\n flat[nestedFlatKey] = convertValueIfNumeric(nestedFlatKey, nestedValue);\n }\n }\n } else {\n flat[flatKey] = convertValueIfNumeric(flatKey, fieldValue);\n }\n }\n }\n\n return flat;\n}\n\n// ============================================\n// UTILITY FUNCTIONS\n// ============================================\n\n/**\n * Convert camelCase to snake_case\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Map top-level API response keys to GAQL resource names\n */\nfunction camelToSnakeResource(key: string): string {\n const map: Record<string, string> = {\n campaign: \"campaign\",\n adGroup: \"ad_group\",\n adGroupAd: \"ad_group_ad\",\n adGroupCriterion: \"ad_group_criterion\",\n adGroupAdAssetView: \"ad_group_ad_asset_view\",\n adGroupAsset: \"ad_group_asset\",\n campaignAsset: \"campaign_asset\",\n keywordView: \"keyword_view\",\n searchTermView: \"search_term_view\",\n dynamicSearchAdsSearchTermView: \"dynamic_search_ads_search_term_view\",\n paidOrganicSearchTermView: \"paid_organic_search_term_view\",\n shoppingPerformanceView: \"shopping_performance_view\",\n shoppingProduct: \"shopping_product\",\n geographicView: \"geographic_view\",\n userLocationView: \"user_location_view\",\n landingPageView: \"landing_page_view\",\n expandedLandingPageView: \"expanded_landing_page_view\",\n detailPlacementView: \"detail_placement_view\",\n managedPlacementView: \"managed_placement_view\",\n topicView: \"topic_view\",\n displayKeywordView: \"display_keyword_view\",\n campaignAudienceView: \"campaign_audience_view\",\n adGroupAudienceView: \"ad_group_audience_view\",\n ageRangeView: \"age_range_view\",\n genderView: \"gender_view\",\n parentalStatusView: \"parental_status_view\",\n incomeRangeView: \"income_range_view\",\n campaignCriterion: \"campaign_criterion\",\n assetGroup: \"asset_group\",\n assetGroupAsset: \"asset_group_asset\",\n assetGroupListingGroupFilter: \"asset_group_listing_group_filter\",\n assetGroupSignal: \"asset_group_signal\",\n campaignSearchTermInsight: \"campaign_search_term_insight\",\n video: \"video\",\n customer: \"customer\",\n customerClient: \"customer_client\",\n campaignBudget: \"campaign_budget\",\n biddingStrategy: \"bidding_strategy\",\n campaignSimulation: \"campaign_simulation\",\n adGroupSimulation: \"ad_group_simulation\",\n biddingStrategySimulation: \"bidding_strategy_simulation\",\n conversionAction: \"conversion_action\",\n changeEvent: \"change_event\",\n changeStatus: \"change_status\",\n label: \"label\",\n experiment: \"experiment\",\n metrics: \"metrics\",\n segments: \"segments\",\n };\n return map[key] || camelToSnake(key);\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { formatMcpToolError } from \"../../core/errors.js\";\nimport type { GoogleAdsClient } from \"./client.js\";\nimport type {\n GenerateKeywordForecastMetricsRequest,\n GenerateKeywordHistoricalMetricsRequest,\n GenerateKeywordHistoricalMetricsResult,\n GenerateKeywordIdeaResult,\n GenerateKeywordIdeasRequest,\n KeywordForecastMetrics,\n KeywordMatchType,\n KeywordPlanAggregateMetricResults,\n KeywordPlanHistoricalMetrics,\n KeywordPlanMonthlySearchVolume,\n KeywordPlanNetwork,\n KeywordPlanYearMonth,\n MonthOfYear,\n} from \"./types.js\";\n\ntype ToolSuccessFormatter = (data: unknown) => {\n content: Array<{ type: \"text\"; text: string }>;\n};\n\ntype BiddingStrategy = \"MANUAL_CPC\" | \"MAXIMIZE_CLICKS\" | \"MAXIMIZE_CONVERSIONS\";\nconst FORECAST_WITH_BREAKDOWN_TIME_BUDGET_MS = 45_000;\n\nexport interface KeywordPlannerHistoryInput {\n keywords: string[];\n geoTargetIds?: string[];\n languageId?: string;\n network?: KeywordPlanNetwork;\n includeAdultKeywords?: boolean;\n includeAverageCpc?: boolean;\n includeDeviceBreakdown?: boolean;\n historyMonths?: number;\n startYearMonth?: string;\n endYearMonth?: string;\n}\n\nexport interface KeywordPlannerIdeasInput {\n seedKeywords?: string[];\n url?: string;\n site?: string;\n geoTargetIds?: string[];\n languageId?: string;\n network?: KeywordPlanNetwork;\n includeAdultKeywords?: boolean;\n includeAverageCpc?: boolean;\n includeDeviceBreakdown?: boolean;\n includeKeywordConcepts?: boolean;\n historyMonths?: number;\n startYearMonth?: string;\n endYearMonth?: string;\n pageSize?: number;\n pageToken?: string;\n}\n\nexport interface KeywordPlannerForecastInput {\n keywords: string[];\n matchType?: KeywordMatchType;\n negativeKeywords?: string[];\n negativeMatchType?: KeywordMatchType;\n geoTargetIds?: string[];\n languageIds?: string[];\n network?: KeywordPlanNetwork;\n biddingStrategy?: BiddingStrategy;\n maxCpcBid?: number;\n dailyBudget?: number;\n maxCpcBidCeiling?: number;\n conversionRate?: number;\n currencyCode?: string;\n startDate?: string;\n endDate?: string;\n}\n\nconst MONTHS: MonthOfYear[] = [\n \"JANUARY\",\n \"FEBRUARY\",\n \"MARCH\",\n \"APRIL\",\n \"MAY\",\n \"JUNE\",\n \"JULY\",\n \"AUGUST\",\n \"SEPTEMBER\",\n \"OCTOBER\",\n \"NOVEMBER\",\n \"DECEMBER\",\n];\n\nconst MONTH_NUMBER = new Map<MonthOfYear, number>(\n MONTHS.map((month, index) => [month, index + 1])\n);\n\nconst customerIdSchema = z.string().regex(/^\\d[\\d-]*\\d$|^\\d$/, \"Expected a numeric Google Ads customer ID, with or without dashes\");\nconst numericIdSchema = z.string().regex(/^\\d+$/, \"Expected a numeric Google Ads criterion ID\");\nconst keywordTextSchema = z.string().trim().min(1).max(80).refine(\n (value) => value.split(/\\s+/).length <= 10,\n \"Google Ads keywords can contain at most 10 words\"\n);\nconst yearMonthSchema = z.string().regex(/^\\d{4}-(0[1-9]|1[0-2])$/, \"Expected YYYY-MM\");\nconst isoDateSchema = z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"Expected YYYY-MM-DD\");\nconst networkSchema = z.enum([\"GOOGLE_SEARCH\", \"GOOGLE_SEARCH_AND_PARTNERS\"]);\nconst matchTypeSchema = z.enum([\"EXACT\", \"PHRASE\", \"BROAD\"]);\nconst currencyAmountSchema = z.number().finite().min(0.000001).max(1_000_000_000);\n\nfunction compactObject<T extends Record<string, unknown>>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, fieldValue]) => fieldValue !== undefined)\n ) as T;\n}\n\nfunction finiteNumber(value: unknown): number | null {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n }\n return null;\n}\n\nfunction safeIntegerNumber(value: unknown): number | null {\n const parsed = finiteNumber(value);\n return parsed !== null && Number.isSafeInteger(parsed) ? parsed : null;\n}\n\nfunction round(value: number, decimals = 2): number {\n const factor = 10 ** decimals;\n return Math.round((value + Number.EPSILON) * factor) / factor;\n}\n\nfunction percentChange(current: number | null, baseline: number | null): number | null {\n if (current === null || baseline === null) return null;\n if (baseline === 0) return current === 0 ? 0 : null;\n return round(((current - baseline) / baseline) * 100);\n}\n\nfunction changeUnavailableReason(\n current: number | null,\n baseline: number | null\n): \"LATEST_VALUE_UNAVAILABLE\" | \"BASELINE_VALUE_UNAVAILABLE\" | \"BASELINE_IS_ZERO\" | null {\n if (current === null) return \"LATEST_VALUE_UNAVAILABLE\";\n if (baseline === null) return \"BASELINE_VALUE_UNAVAILABLE\";\n if (baseline === 0 && current !== 0) return \"BASELINE_IS_ZERO\";\n return null;\n}\n\nfunction changeDirection(changePercent: number | null): \"UP\" | \"DOWN\" | \"FLAT\" | \"UNAVAILABLE\" {\n if (changePercent === null) return \"UNAVAILABLE\";\n if (changePercent > 0) return \"UP\";\n if (changePercent < 0) return \"DOWN\";\n return \"FLAT\";\n}\n\nfunction parseCalendarDate(value: string): Date {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(value);\n if (!match) throw new Error(`Invalid date \"${value}\". Expected YYYY-MM-DD.`);\n\n const year = Number(match[1]);\n const month = Number(match[2]);\n const day = Number(match[3]);\n const parsed = new Date(Date.UTC(year, month - 1, day));\n if (\n parsed.getUTCFullYear() !== year\n || parsed.getUTCMonth() !== month - 1\n || parsed.getUTCDate() !== day\n ) {\n throw new Error(`Invalid calendar date \"${value}\".`);\n }\n return parsed;\n}\n\nfunction inclusiveDays(startDate?: string, endDate?: string, now = new Date()): number | null {\n if (!startDate && !endDate) return null;\n if (!startDate || !endDate) {\n throw new Error(\"startDate and endDate must be supplied together.\");\n }\n const start = parseCalendarDate(startDate);\n const end = parseCalendarDate(endDate);\n if (start > end) throw new Error(\"startDate must be on or before endDate.\");\n\n const today = new Date(Date.UTC(\n now.getUTCFullYear(),\n now.getUTCMonth(),\n now.getUTCDate()\n ));\n if (start <= today) {\n throw new Error(\"startDate must be in the future. Google evaluates this in the customer account time zone; use at least tomorrow and allow an extra day near time-zone boundaries.\");\n }\n const latestAllowedEnd = new Date(today);\n latestAllowedEnd.setUTCFullYear(latestAllowedEnd.getUTCFullYear() + 1);\n if (end > latestAllowedEnd) {\n throw new Error(`endDate must be within one year of today (no later than ${latestAllowedEnd.toISOString().slice(0, 10)} for this local preflight).`);\n }\n return Math.floor((end.getTime() - start.getTime()) / 86_400_000) + 1;\n}\n\ninterface ParsedYearMonth {\n year: number;\n month: number;\n}\n\nfunction parseYearMonth(value: string): ParsedYearMonth {\n const match = /^(\\d{4})-(0[1-9]|1[0-2])$/.exec(value);\n if (!match) throw new Error(`Invalid year-month \"${value}\". Expected YYYY-MM.`);\n return { year: Number(match[1]), month: Number(match[2]) };\n}\n\nfunction formatYearMonth(value: ParsedYearMonth): string {\n return `${String(value.year).padStart(4, \"0\")}-${String(value.month).padStart(2, \"0\")}`;\n}\n\nfunction addMonths(value: ParsedYearMonth, months: number): ParsedYearMonth {\n const absoluteMonth = value.year * 12 + value.month - 1 + months;\n return {\n year: Math.floor(absoluteMonth / 12),\n month: ((absoluteMonth % 12) + 12) % 12 + 1,\n };\n}\n\nfunction monthsBetweenInclusive(start: ParsedYearMonth, end: ParsedYearMonth): number {\n return (end.year - start.year) * 12 + end.month - start.month + 1;\n}\n\nfunction toApiYearMonth(value: ParsedYearMonth): KeywordPlanYearMonth {\n const month = MONTHS[value.month - 1];\n if (!month) throw new Error(`Invalid month number ${value.month}.`);\n return { year: String(value.year), month };\n}\n\nexport interface ResolvedHistoryRange {\n startYearMonth: string;\n endYearMonth: string;\n monthCount: number;\n apiRange: { start: KeywordPlanYearMonth; end: KeywordPlanYearMonth };\n}\n\nexport const MAX_KEYWORD_PLANNER_MONTHLY_POINTS = 50_000;\n\nexport function assertKeywordPlannerHistoryPointBudget(\n keywordCount: number,\n monthCount: number\n): number {\n const estimatedMonthlyPointCount = keywordCount * monthCount;\n if (estimatedMonthlyPointCount > MAX_KEYWORD_PLANNER_MONTHLY_POINTS) {\n throw new Error(`This request could make Google return up to ${estimatedMonthlyPointCount.toLocaleString(\"en-US\")} monthly points. Split the keyword list or shorten the history range; includeMonthlySearchVolumes=false only reduces the final MCP payload, not Google's upstream response.`);\n }\n return estimatedMonthlyPointCount;\n}\n\nexport function resolveKeywordPlannerHistoryRange(\n startYearMonth: string | undefined,\n endYearMonth: string | undefined,\n historyMonths = 24,\n now = new Date()\n): ResolvedHistoryRange {\n let start: ParsedYearMonth;\n let end: ParsedYearMonth;\n\n if (startYearMonth || endYearMonth) {\n if (!startYearMonth || !endYearMonth) {\n throw new Error(\"startYearMonth and endYearMonth must be supplied together.\");\n }\n start = parseYearMonth(startYearMonth);\n end = parseYearMonth(endYearMonth);\n } else {\n if (!Number.isInteger(historyMonths) || historyMonths < 3 || historyMonths > 48) {\n throw new Error(\"historyMonths must be an integer between 3 and 48.\");\n }\n const current = { year: now.getUTCFullYear(), month: now.getUTCMonth() + 1 };\n end = addMonths(current, -1);\n start = addMonths(end, -(historyMonths - 1));\n }\n\n const monthCount = monthsBetweenInclusive(start, end);\n if (monthCount < 1) throw new Error(\"startYearMonth must be on or before endYearMonth.\");\n if (monthCount > 48) throw new Error(\"Google Ads historical search metrics support at most 48 months.\");\n\n return {\n startYearMonth: formatYearMonth(start),\n endYearMonth: formatYearMonth(end),\n monthCount,\n apiRange: { start: toApiYearMonth(start), end: toApiYearMonth(end) },\n };\n}\n\nfunction targetingFields(input: {\n geoTargetIds?: string[];\n languageId?: string;\n network?: KeywordPlanNetwork;\n includeAdultKeywords?: boolean;\n}): Pick<\n GenerateKeywordHistoricalMetricsRequest,\n \"geoTargetConstants\" | \"language\" | \"keywordPlanNetwork\" | \"includeAdultKeywords\"\n> {\n return compactObject({\n geoTargetConstants: input.geoTargetIds\n ? [...new Set(input.geoTargetIds)].map((id) => `geoTargetConstants/${id}`)\n : undefined,\n language: input.languageId ? `languageConstants/${input.languageId}` : undefined,\n keywordPlanNetwork: input.network ?? \"GOOGLE_SEARCH\",\n includeAdultKeywords: input.includeAdultKeywords ?? false,\n });\n}\n\nexport function buildKeywordHistoricalMetricsRequest(input: KeywordPlannerHistoryInput): {\n request: GenerateKeywordHistoricalMetricsRequest;\n historyRange: ResolvedHistoryRange;\n} {\n const historyRange = resolveKeywordPlannerHistoryRange(\n input.startYearMonth,\n input.endYearMonth,\n input.historyMonths ?? 24\n );\n\n return {\n historyRange,\n request: {\n keywords: input.keywords,\n ...targetingFields(input),\n historicalMetricsOptions: {\n yearMonthRange: historyRange.apiRange,\n includeAverageCpc: input.includeAverageCpc ?? true,\n },\n ...(input.includeDeviceBreakdown\n ? { aggregateMetrics: { aggregateMetricTypes: [\"DEVICE\"] } }\n : {}),\n },\n };\n}\n\ninterface NormalizedMonthlyVolume {\n date: string;\n year: number;\n month: MonthOfYear;\n monthNumber: number;\n monthlySearches: number | null;\n monthlySearchesRaw: string | null;\n}\n\nexport function normalizeMonthlySearchVolumes(\n volumes: KeywordPlanMonthlySearchVolume[] | undefined\n): NormalizedMonthlyVolume[] {\n const normalized: NormalizedMonthlyVolume[] = [];\n\n for (const volume of volumes ?? []) {\n const year = finiteNumber(volume.year);\n const month = volume.month;\n if (year === null || !month || month === \"UNKNOWN\" || month === \"UNSPECIFIED\") continue;\n const monthNumber = MONTH_NUMBER.get(month);\n if (!monthNumber) continue;\n normalized.push({\n date: `${String(Math.trunc(year)).padStart(4, \"0\")}-${String(monthNumber).padStart(2, \"0\")}`,\n year: Math.trunc(year),\n month,\n monthNumber,\n monthlySearches: safeIntegerNumber(volume.monthlySearches),\n monthlySearchesRaw: volume.monthlySearches == null\n ? null\n : String(volume.monthlySearches),\n });\n }\n\n return normalized.sort((a, b) => a.date.localeCompare(b.date));\n}\n\nexport function computeKeywordPlannerTrends(volumes: KeywordPlanMonthlySearchVolume[] | undefined) {\n const monthly = normalizeMonthlySearchVolumes(volumes);\n const latest = [...monthly].reverse().find((volume) => volume.monthlySearches !== null) ?? null;\n if (!latest) {\n return {\n latestMonth: null,\n threeMonthBaseline: null,\n threeMonthChangePercent: null,\n threeMonthDirection: \"UNAVAILABLE\" as const,\n threeMonthChangeUnavailableReason: \"LATEST_VALUE_UNAVAILABLE\" as const,\n yearAgoBaseline: null,\n yearOverYearChangePercent: null,\n yearOverYearDirection: \"UNAVAILABLE\" as const,\n yearOverYearChangeUnavailableReason: \"LATEST_VALUE_UNAVAILABLE\" as const,\n latest12Months: null,\n previous12Months: null,\n rolling12MonthYearOverYearChangePercent: null,\n };\n }\n\n const latestYearMonth = parseYearMonth(latest.date);\n const threeMonthDate = formatYearMonth(addMonths(latestYearMonth, -2));\n const yearAgoDate = formatYearMonth(addMonths(latestYearMonth, -12));\n const threeMonthBaseline = monthly.find((volume) => volume.date === threeMonthDate) ?? null;\n const yearAgoBaseline = monthly.find((volume) => volume.date === yearAgoDate) ?? null;\n const threeMonthChangePercent = percentChange(\n latest.monthlySearches,\n threeMonthBaseline?.monthlySearches ?? null\n );\n const yearOverYearChangePercent = percentChange(\n latest.monthlySearches,\n yearAgoBaseline?.monthlySearches ?? null\n );\n const summarizeWindow = (startOffset: number, endOffset: number) => {\n const startDate = formatYearMonth(addMonths(latestYearMonth, startOffset));\n const endDate = formatYearMonth(addMonths(latestYearMonth, endOffset));\n const values = monthly\n .filter((volume) => volume.date >= startDate && volume.date <= endDate)\n .map((volume) => volume.monthlySearches)\n .filter((value): value is number => value !== null);\n const total = values.reduce((sum, value) => sum + value, 0);\n return {\n startDate,\n endDate,\n availableMonthCount: values.length,\n totalSearches: values.length > 0 ? total : null,\n averageMonthlySearches: values.length > 0 ? round(total / values.length, 2) : null,\n };\n };\n const latest12Months = summarizeWindow(-11, 0);\n const previous12Months = summarizeWindow(-23, -12);\n const rolling12MonthYearOverYearChangePercent = latest12Months.availableMonthCount === 12\n && previous12Months.availableMonthCount === 12\n ? percentChange(\n latest12Months.averageMonthlySearches,\n previous12Months.averageMonthlySearches\n )\n : null;\n\n return {\n latestMonth: { date: latest.date, searches: latest.monthlySearches },\n threeMonthBaseline: threeMonthBaseline\n ? { date: threeMonthBaseline.date, searches: threeMonthBaseline.monthlySearches }\n : null,\n threeMonthChangePercent,\n threeMonthDirection: changeDirection(threeMonthChangePercent),\n threeMonthChangeUnavailableReason: changeUnavailableReason(\n latest.monthlySearches,\n threeMonthBaseline?.monthlySearches ?? null\n ),\n yearAgoBaseline: yearAgoBaseline\n ? { date: yearAgoBaseline.date, searches: yearAgoBaseline.monthlySearches }\n : null,\n yearOverYearChangePercent,\n yearOverYearDirection: changeDirection(yearOverYearChangePercent),\n yearOverYearChangeUnavailableReason: changeUnavailableReason(\n latest.monthlySearches,\n yearAgoBaseline?.monthlySearches ?? null\n ),\n latest12Months,\n previous12Months,\n rolling12MonthYearOverYearChangePercent,\n };\n}\n\nfunction normalizeMicros(value: string | undefined) {\n const amount = finiteNumber(value);\n return {\n micros: value ?? null,\n amount: amount === null ? null : amount / 1_000_000,\n };\n}\n\nexport function normalizeKeywordHistoricalMetrics(\n metrics: KeywordPlanHistoricalMetrics | undefined,\n includeMonthlySearchVolumes = true\n) {\n const monthlySearchVolumes = normalizeMonthlySearchVolumes(metrics?.monthlySearchVolumes);\n return {\n avgMonthlySearches: safeIntegerNumber(metrics?.avgMonthlySearches),\n avgMonthlySearchesRaw: metrics?.avgMonthlySearches ?? null,\n competition: metrics?.competition ?? null,\n competitionIndex: safeIntegerNumber(metrics?.competitionIndex),\n competitionIndexRaw: metrics?.competitionIndex ?? null,\n lowTopOfPageBid: normalizeMicros(metrics?.lowTopOfPageBidMicros),\n highTopOfPageBid: normalizeMicros(metrics?.highTopOfPageBidMicros),\n averageCpc: normalizeMicros(metrics?.averageCpcMicros),\n monthlySearchVolumes: includeMonthlySearchVolumes ? monthlySearchVolumes : undefined,\n trends: computeKeywordPlannerTrends(metrics?.monthlySearchVolumes),\n };\n}\n\nfunction normalizeHistoricalResult(\n result: GenerateKeywordHistoricalMetricsResult,\n includeMonthlySearchVolumes: boolean\n) {\n return {\n keyword: result.text ?? null,\n closeVariants: result.closeVariants ?? [],\n metrics: normalizeKeywordHistoricalMetrics(\n result.keywordMetrics,\n includeMonthlySearchVolumes\n ),\n };\n}\n\nfunction normalizeIdeaResult(result: GenerateKeywordIdeaResult) {\n return {\n keyword: result.text ?? null,\n closeVariants: result.closeVariants ?? [],\n metrics: normalizeKeywordHistoricalMetrics(result.keywordIdeaMetrics),\n concepts: result.keywordAnnotations?.concepts ?? [],\n };\n}\n\nfunction normalizeAggregateMetrics(results: KeywordPlanAggregateMetricResults | undefined) {\n return {\n deviceSearches: (results?.deviceSearches ?? []).map((entry) => ({\n device: entry.device ?? null,\n searches: safeIntegerNumber(entry.searchCount),\n searchesRaw: entry.searchCount ?? null,\n })),\n };\n}\n\nexport function buildKeywordIdeasRequest(input: KeywordPlannerIdeasInput): {\n request: GenerateKeywordIdeasRequest;\n historyRange: ResolvedHistoryRange;\n seedType: \"KEYWORD\" | \"URL\" | \"KEYWORD_AND_URL\" | \"SITE\";\n} {\n const hasKeywords = (input.seedKeywords?.length ?? 0) > 0;\n const hasUrl = Boolean(input.url);\n const hasSite = Boolean(input.site);\n\n if (hasSite && (hasKeywords || hasUrl)) {\n throw new Error(\"site is an exclusive seed; do not combine it with seedKeywords or url.\");\n }\n if (!hasSite && !hasKeywords && !hasUrl) {\n throw new Error(\"Provide seedKeywords, url, both seedKeywords and url, or site.\");\n }\n\n const historyRange = resolveKeywordPlannerHistoryRange(\n input.startYearMonth,\n input.endYearMonth,\n input.historyMonths ?? 13\n );\n let seed: Pick<\n GenerateKeywordIdeasRequest,\n \"keywordSeed\" | \"urlSeed\" | \"keywordAndUrlSeed\" | \"siteSeed\"\n >;\n let seedType: \"KEYWORD\" | \"URL\" | \"KEYWORD_AND_URL\" | \"SITE\";\n\n if (hasSite) {\n seed = { siteSeed: { site: input.site! } };\n seedType = \"SITE\";\n } else if (hasKeywords && hasUrl) {\n seed = { keywordAndUrlSeed: { keywords: input.seedKeywords!, url: input.url! } };\n seedType = \"KEYWORD_AND_URL\";\n } else if (hasKeywords) {\n seed = { keywordSeed: { keywords: input.seedKeywords! } };\n seedType = \"KEYWORD\";\n } else {\n seed = { urlSeed: { url: input.url! } };\n seedType = \"URL\";\n }\n\n return {\n historyRange,\n seedType,\n request: compactObject({\n ...targetingFields(input),\n ...seed,\n historicalMetricsOptions: {\n yearMonthRange: historyRange.apiRange,\n includeAverageCpc: input.includeAverageCpc ?? true,\n },\n aggregateMetrics: input.includeDeviceBreakdown\n ? { aggregateMetricTypes: [\"DEVICE\" as const] }\n : undefined,\n keywordAnnotation: input.includeKeywordConcepts === false ? undefined : [\"KEYWORD_CONCEPT\" as const],\n pageSize: input.pageSize ?? 100,\n pageToken: input.pageToken,\n }),\n };\n}\n\nfunction currencyToMicros(value: number): string {\n const unroundedMicros = value * 1_000_000;\n const micros = Math.round(unroundedMicros);\n if (Math.abs(unroundedMicros - micros) > 0.000001) {\n throw new Error(`Currency amount ${value} has more than six decimal places and cannot be represented exactly in micros.`);\n }\n if (micros < 1) {\n throw new Error(`Currency amount ${value} is smaller than one micro unit.`);\n }\n if (!Number.isSafeInteger(micros)) {\n throw new Error(`Currency amount ${value} is too large to convert safely to micros.`);\n }\n return String(micros);\n}\n\nexport function buildKeywordForecastRequest(\n input: KeywordPlannerForecastInput,\n now = new Date()\n): {\n request: GenerateKeywordForecastMetricsRequest;\n periodDays: number | null;\n} {\n const biddingStrategy = input.biddingStrategy ?? \"MANUAL_CPC\";\n const matchType = input.matchType ?? \"BROAD\";\n const negativeMatchType = input.negativeMatchType ?? \"BROAD\";\n const periodDays = inclusiveDays(input.startDate, input.endDate, now);\n let apiBiddingStrategy: GenerateKeywordForecastMetricsRequest[\"campaign\"][\"biddingStrategy\"];\n\n if (biddingStrategy === \"MANUAL_CPC\") {\n if (input.maxCpcBid === undefined) {\n throw new Error(\"maxCpcBid is required when biddingStrategy is MANUAL_CPC.\");\n }\n apiBiddingStrategy = {\n manualCpcBiddingStrategy: compactObject({\n maxCpcBidMicros: currencyToMicros(input.maxCpcBid),\n dailyBudgetMicros: input.dailyBudget === undefined\n ? undefined\n : currencyToMicros(input.dailyBudget),\n }),\n };\n } else if (biddingStrategy === \"MAXIMIZE_CLICKS\") {\n if (input.dailyBudget === undefined) {\n throw new Error(\"dailyBudget is required when biddingStrategy is MAXIMIZE_CLICKS.\");\n }\n apiBiddingStrategy = {\n maximizeClicksBiddingStrategy: compactObject({\n dailyTargetSpendMicros: currencyToMicros(input.dailyBudget),\n maxCpcBidCeilingMicros: input.maxCpcBidCeiling === undefined\n ? undefined\n : currencyToMicros(input.maxCpcBidCeiling),\n }),\n };\n } else {\n if (input.dailyBudget === undefined) {\n throw new Error(\"dailyBudget is required when biddingStrategy is MAXIMIZE_CONVERSIONS.\");\n }\n apiBiddingStrategy = {\n maximizeConversionsBiddingStrategy: {\n dailyTargetSpendMicros: currencyToMicros(input.dailyBudget),\n },\n };\n }\n\n return {\n periodDays,\n request: compactObject({\n currencyCode: input.currencyCode,\n forecastPeriod: input.startDate && input.endDate\n ? { startDate: input.startDate, endDate: input.endDate }\n : undefined,\n campaign: compactObject({\n keywordPlanNetwork: input.network ?? \"GOOGLE_SEARCH\",\n biddingStrategy: apiBiddingStrategy,\n adGroups: [{\n biddableKeywords: input.keywords.map((text) => ({\n keyword: { text, matchType },\n })),\n }],\n geoModifiers: input.geoTargetIds\n ? [...new Set(input.geoTargetIds)].map((id) => ({\n geoTargetConstant: `geoTargetConstants/${id}`,\n }))\n : undefined,\n languageConstants: input.languageIds\n ? [...new Set(input.languageIds)].map((id) => `languageConstants/${id}`)\n : undefined,\n negativeKeywords: input.negativeKeywords?.map((text) => ({\n text,\n matchType: negativeMatchType,\n })),\n conversionRate: input.conversionRate,\n }),\n }),\n };\n}\n\nexport function normalizeKeywordForecastMetrics(\n metrics: KeywordForecastMetrics | undefined,\n periodDays: number | null\n) {\n const impressions = finiteNumber(metrics?.impressions);\n const clicks = finiteNumber(metrics?.clicks);\n const conversions = finiteNumber(metrics?.conversions);\n const cost = normalizeMicros(metrics?.costMicros);\n const averageCpc = normalizeMicros(metrics?.averageCpcMicros);\n const averageCpa = normalizeMicros(metrics?.averageCpaMicros);\n\n return {\n impressions,\n clicks,\n cost,\n clickThroughRate: finiteNumber(metrics?.clickThroughRate),\n clickThroughRatePercent: metrics?.clickThroughRate === undefined\n ? null\n : round(metrics.clickThroughRate * 100, 4),\n averageCpc,\n conversions,\n conversionRate: finiteNumber(metrics?.conversionRate),\n conversionRatePercent: metrics?.conversionRate === undefined\n ? null\n : round(metrics.conversionRate * 100, 4),\n averageCpa,\n dailyAverages: periodDays === null\n ? null\n : {\n days: periodDays,\n impressions: impressions === null ? null : round(impressions / periodDays, 4),\n clicks: clicks === null ? null : round(clicks / periodDays, 4),\n cost: cost.amount === null ? null : round(cost.amount / periodDays, 6),\n conversions: conversions === null ? null : round(conversions / periodDays, 4),\n },\n };\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nconst trendDefinitions = {\n threeMonthChangePercent: \"Percent change between the latest available month and the month two calendar months earlier, matching the Keyword Planner UI definition.\",\n yearOverYearChangePercent: \"Percent change between the latest available month and the same calendar month one year earlier.\",\n rolling12MonthYearOverYearChangePercent: \"Derived change between the average of the latest 12 available calendar slots and the preceding 12-month window; this is additional analysis, not the UI YoY column.\",\n unavailableChange: \"A percent is null when a baseline month is missing/null or is zero while the latest value is non-zero.\",\n};\n\nexport function registerGoogleAdsKeywordPlannerTools(\n server: McpServer,\n client: GoogleAdsClient,\n ok: ToolSuccessFormatter\n): void {\n server.tool(\n \"google_ads_generate_keyword_historical_metrics\",\n \"Get Keyword Planner search-volume history for supplied keywords. Returns average monthly searches, monthly volumes (up to 48 months), latest volume, computed 3-month and YoY changes, competition, CPC/bid ranges, close variants, and optional device totals. This is a read-only planless RPC.\",\n {\n customerId: customerIdSchema.describe(\"Google Ads serving customer ID; use the client account, not its MCC manager\"),\n keywords: z.array(keywordTextSchema).min(1).max(10_000).describe(\"Keywords to analyze; Google may combine near-exact close variants\"),\n geoTargetIds: z.array(numericIdSchema).max(10).optional().default([]).describe(\"Geo target criterion IDs, e.g. 2250 for France; empty means all geographies\"),\n languageId: numericIdSchema.optional().describe(\"Optional language criterion ID, e.g. 1002 for French; omit for all languages\"),\n network: networkSchema.optional().default(\"GOOGLE_SEARCH\"),\n includeAdultKeywords: z.boolean().optional().default(false),\n includeAverageCpc: z.boolean().optional().default(true).describe(\"Request legacy average CPC in addition to top-of-page bid ranges\"),\n includeDeviceBreakdown: z.boolean().optional().default(false).describe(\"Return aggregate searches by device across all requested keywords\"),\n includeMonthlySearchVolumes: z.boolean().optional().default(true).describe(\"Include every monthly point in the MCP output. Set false to trim an allowed response; Google's upstream series and the 50,000-point guard are unchanged\"),\n historyMonths: z.number().int().min(3).max(48).optional().default(24).describe(\"History length when no explicit YYYY-MM range is supplied; 24 enables YoY calculation\"),\n startYearMonth: yearMonthSchema.optional().describe(\"Optional inclusive historical range start YYYY-MM\"),\n endYearMonth: yearMonthSchema.optional().describe(\"Optional inclusive historical range end YYYY-MM\"),\n },\n async ({\n customerId,\n keywords,\n geoTargetIds,\n languageId,\n network,\n includeAdultKeywords,\n includeAverageCpc,\n includeDeviceBreakdown,\n includeMonthlySearchVolumes,\n historyMonths,\n startYearMonth,\n endYearMonth,\n }) => {\n try {\n const startedAt = Date.now();\n const { request, historyRange } = buildKeywordHistoricalMetricsRequest({\n keywords,\n geoTargetIds,\n languageId,\n network,\n includeAdultKeywords,\n includeAverageCpc,\n includeDeviceBreakdown,\n historyMonths,\n startYearMonth,\n endYearMonth,\n });\n const estimatedMonthlyPointCount = assertKeywordPlannerHistoryPointBudget(\n keywords.length,\n historyRange.monthCount\n );\n const response = await client.generateKeywordHistoricalMetrics(customerId, request);\n const results = (response.results ?? []).map((result) => normalizeHistoricalResult(\n result,\n includeMonthlySearchVolumes\n ));\n const warnings: string[] = [];\n if (results.length < keywords.length) {\n warnings.push(\"Google returned fewer rows than requested keywords because near-exact close variants are de-duplicated and some keywords can lack data.\");\n }\n\n return ok({\n dataKind: \"historical_search_volume\",\n isApproximate: true,\n updatedMonthly: true,\n results,\n count: results.length,\n requestedKeywordCount: keywords.length,\n estimatedMonthlyPointCount,\n historyRange: {\n startYearMonth: historyRange.startYearMonth,\n endYearMonth: historyRange.endYearMonth,\n monthCount: historyRange.monthCount,\n },\n targeting: {\n geoTargetIds,\n languageId: languageId ?? null,\n network,\n },\n aggregateMetrics: normalizeAggregateMetrics(response.aggregateMetricResults),\n trendDefinitions,\n derivedFields: [\n \"results[].metrics.trends.threeMonthChangePercent\",\n \"results[].metrics.trends.yearOverYearChangePercent\",\n \"results[].metrics.trends.rolling12MonthYearOverYearChangePercent\",\n ],\n currency: \"Bid and CPC amount fields are in the serving customer account currency; micros are also preserved as strings.\",\n warnings,\n limitations: [\n \"Search volumes and forecast-style bid values are Google estimates, not observed campaign impressions.\",\n \"Ad impression share shown in some Keyword Planner UI views is not exposed by GenerateKeywordHistoricalMetrics; use keyword performance reporting for serving keywords.\",\n ],\n nextActions: [\n \"Use google_ads_generate_keyword_forecast_metrics to estimate impressions, clicks, and cost for a selected keyword set.\",\n \"Use google_ads_generate_keyword_ideas to expand the keyword list.\",\n ],\n debug: { requestCount: 1, executionTimeMs: Date.now() - startedAt },\n });\n } catch (error) {\n return formatMcpToolError(error);\n }\n }\n );\n\n server.tool(\n \"google_ads_generate_keyword_ideas\",\n \"Discover Keyword Planner ideas from up to 20 seed keywords, a URL, keywords plus URL, or a whole site. Returns historical volume, monthly trends, computed 3-month/YoY changes, competition, bids, close variants, optional concepts, and pagination. Read-only; no plan is saved.\",\n {\n customerId: customerIdSchema.describe(\"Google Ads serving customer ID; use the client account, not its MCC manager\"),\n seedKeywords: z.array(keywordTextSchema).min(1).max(20).optional(),\n url: z.string().trim().min(1).optional().describe(\"Specific page URL to crawl; combine with seedKeywords if desired\"),\n site: z.string().trim().min(1).optional().describe(\"Whole-domain seed; exclusive with seedKeywords/url\"),\n geoTargetIds: z.array(numericIdSchema).max(10).optional().default([]),\n languageId: numericIdSchema.optional(),\n network: networkSchema.optional().default(\"GOOGLE_SEARCH\"),\n includeAdultKeywords: z.boolean().optional().default(false),\n includeAverageCpc: z.boolean().optional().default(true),\n includeDeviceBreakdown: z.boolean().optional().default(false),\n includeKeywordConcepts: z.boolean().optional().default(true),\n historyMonths: z.number().int().min(3).max(48).optional().default(13).describe(\"13 months is enough to compute latest-month YoY while limiting response size\"),\n startYearMonth: yearMonthSchema.optional(),\n endYearMonth: yearMonthSchema.optional(),\n pageSize: z.number().int().min(1).max(10_000).optional().default(100).describe(\"Google supports up to 10,000; keep pages small for interactive MCP use and use larger pages only for controlled exports\"),\n pageToken: z.string().optional().describe(\"nextPageToken from a prior identical request\"),\n },\n async ({\n customerId,\n seedKeywords,\n url,\n site,\n geoTargetIds,\n languageId,\n network,\n includeAdultKeywords,\n includeAverageCpc,\n includeDeviceBreakdown,\n includeKeywordConcepts,\n historyMonths,\n startYearMonth,\n endYearMonth,\n pageSize,\n pageToken,\n }) => {\n try {\n const startedAt = Date.now();\n const { request, historyRange, seedType } = buildKeywordIdeasRequest({\n seedKeywords,\n url,\n site,\n geoTargetIds,\n languageId,\n network,\n includeAdultKeywords,\n includeAverageCpc,\n includeDeviceBreakdown,\n includeKeywordConcepts,\n historyMonths,\n startYearMonth,\n endYearMonth,\n pageSize,\n pageToken,\n });\n const estimatedMonthlyPointCount = assertKeywordPlannerHistoryPointBudget(\n pageSize,\n historyRange.monthCount\n );\n const response = await client.generateKeywordIdeas(customerId, request);\n const ideas = (response.results ?? []).map(normalizeIdeaResult);\n const nextPageToken = response.nextPageToken ?? null;\n\n return ok({\n dataKind: \"keyword_ideas_with_historical_search_volume\",\n isApproximate: true,\n updatedMonthly: true,\n ideas,\n count: ideas.length,\n estimatedMonthlyPointCount,\n totalSize: safeIntegerNumber(response.totalSize),\n totalSizeRaw: response.totalSize ?? null,\n nextPageToken,\n seedType,\n historyRange: {\n startYearMonth: historyRange.startYearMonth,\n endYearMonth: historyRange.endYearMonth,\n monthCount: historyRange.monthCount,\n },\n targeting: { geoTargetIds, languageId: languageId ?? null, network },\n aggregateMetrics: normalizeAggregateMetrics(response.aggregateMetricResults),\n trendDefinitions,\n derivedFields: [\n \"ideas[].metrics.trends.threeMonthChangePercent\",\n \"ideas[].metrics.trends.yearOverYearChangePercent\",\n \"ideas[].metrics.trends.rolling12MonthYearOverYearChangePercent\",\n ],\n currency: \"Bid and CPC amount fields are in the serving customer account currency; micros are also preserved as strings.\",\n warnings: nextPageToken\n ? [\"More keyword ideas are available; repeat the same request with nextPageToken as pageToken.\"]\n : [],\n limitations: [\n \"Google can canonicalize ideas and combine close variants.\",\n \"Keep every request field identical when following a page token.\",\n ],\n nextActions: [\n \"Pass selected ideas to google_ads_generate_keyword_historical_metrics for a focused history table.\",\n \"Forecast the final list with google_ads_generate_keyword_forecast_metrics.\",\n ],\n debug: { requestCount: 1, executionTimeMs: Date.now() - startedAt },\n });\n } catch (error) {\n return formatMcpToolError(error);\n }\n }\n );\n\n server.tool(\n \"google_ads_generate_keyword_forecast_metrics\",\n \"Forecast impressions, clicks, CTR, CPC, cost, conversions, and CPA for a temporary keyword campaign. Supports targeting, negatives, match type, three bidding strategies, explicit future dates, and an optional independent per-keyword breakdown. This read-only planless RPC does not create a campaign or saved plan.\",\n {\n customerId: customerIdSchema.describe(\"Google Ads serving customer ID; use a relevant client account for better estimates\"),\n keywords: z.array(keywordTextSchema).min(1).max(1000),\n matchType: matchTypeSchema.optional().default(\"BROAD\"),\n negativeKeywords: z.array(keywordTextSchema).max(1000).optional().default([]),\n negativeMatchType: matchTypeSchema.optional().default(\"BROAD\"),\n geoTargetIds: z.array(numericIdSchema).max(20).optional().default([]),\n languageIds: z.array(numericIdSchema).max(10).optional().default([]),\n network: networkSchema.optional().default(\"GOOGLE_SEARCH\"),\n biddingStrategy: z.enum([\"MANUAL_CPC\", \"MAXIMIZE_CLICKS\", \"MAXIMIZE_CONVERSIONS\"]).optional().default(\"MANUAL_CPC\"),\n maxCpcBid: currencyAmountSchema.optional().describe(\"Bid in standard account-currency units; required for MANUAL_CPC\"),\n dailyBudget: currencyAmountSchema.optional().describe(\"Daily amount in standard currency units; required for maximize strategies and optional for MANUAL_CPC\"),\n maxCpcBidCeiling: currencyAmountSchema.optional().describe(\"Optional standard-currency CPC ceiling for MAXIMIZE_CLICKS\"),\n conversionRate: z.number().finite().min(0).max(1).optional().describe(\"Expected conversion rate as a decimal, e.g. 0.02 for 2%\"),\n currencyCode: z.string().regex(/^[A-Z]{3}$/).optional().describe(\"Optional ISO 4217 conversion currency; account currency is used by default\"),\n startDate: isoDateSchema.optional().describe(\"Optional inclusive future forecast start YYYY-MM-DD; supply with endDate\"),\n endDate: isoDateSchema.optional().describe(\"Optional inclusive forecast end YYYY-MM-DD, no more than one year ahead; supply with startDate\"),\n includeKeywordBreakdown: z.boolean().optional().default(false).describe(\"Make independent one-keyword forecast calls in addition to the combined campaign forecast\"),\n keywordBreakdownLimit: z.number().int().min(1).max(20).optional().default(10).describe(\"Safety cap for additional rate-limited Keyword Planner requests\"),\n },\n async ({\n customerId,\n keywords,\n matchType,\n negativeKeywords,\n negativeMatchType,\n geoTargetIds,\n languageIds,\n network,\n biddingStrategy,\n maxCpcBid,\n dailyBudget,\n maxCpcBidCeiling,\n conversionRate,\n currencyCode,\n startDate,\n endDate,\n includeKeywordBreakdown,\n keywordBreakdownLimit,\n }) => {\n try {\n const startedAt = Date.now();\n const baseInput: KeywordPlannerForecastInput = {\n keywords,\n matchType,\n negativeKeywords,\n negativeMatchType,\n geoTargetIds,\n languageIds,\n network,\n biddingStrategy,\n maxCpcBid,\n dailyBudget,\n maxCpcBidCeiling,\n conversionRate,\n currencyCode,\n startDate,\n endDate,\n };\n const { request, periodDays } = buildKeywordForecastRequest(baseInput);\n const deadlineAtMs = includeKeywordBreakdown\n ? startedAt + FORECAST_WITH_BREAKDOWN_TIME_BUDGET_MS\n : undefined;\n const response = await client.generateKeywordForecastMetrics(\n customerId,\n request,\n deadlineAtMs\n );\n const campaignForecast = normalizeKeywordForecastMetrics(\n response.campaignForecastMetrics,\n periodDays\n );\n const warnings: string[] = [];\n const keywordForecasts: Array<Record<string, unknown>> = [];\n let requestCount = 1;\n\n if (includeKeywordBreakdown) {\n const breakdownKeywords = keywords.slice(0, keywordBreakdownLimit);\n if (keywords.length > keywordBreakdownLimit) {\n warnings.push(`Independent keyword breakdown was capped at ${keywordBreakdownLimit} of ${keywords.length} keywords to protect the rate-limited planning quota.`);\n }\n\n if (keywords.length === 1) {\n keywordForecasts.push({ keyword: keywords[0], metrics: campaignForecast });\n } else {\n for (const keyword of breakdownKeywords) {\n if (deadlineAtMs !== undefined && Date.now() >= deadlineAtMs) {\n warnings.push(\"Independent keyword breakdown stopped because the 45-second forecast tool time budget was exhausted; partial results are returned.\");\n break;\n }\n requestCount += 1;\n try {\n const single = buildKeywordForecastRequest({ ...baseInput, keywords: [keyword] });\n const singleResponse = await client.generateKeywordForecastMetrics(\n customerId,\n single.request,\n deadlineAtMs\n );\n keywordForecasts.push({\n keyword,\n metrics: normalizeKeywordForecastMetrics(\n singleResponse.campaignForecastMetrics,\n single.periodDays\n ),\n });\n } catch (error) {\n keywordForecasts.push({ keyword, error: errorMessage(error) });\n warnings.push(`Independent forecast failed for \"${keyword}\"; the combined forecast remains available.`);\n if (deadlineAtMs !== undefined && Date.now() >= deadlineAtMs) {\n warnings.push(\"Independent keyword breakdown stopped because the 45-second forecast tool time budget was exhausted; partial results are returned.\");\n break;\n }\n }\n }\n }\n }\n\n return ok({\n dataKind: \"keyword_campaign_forecast\",\n aggregationLevel: \"campaign\",\n isEstimate: true,\n campaignForecast,\n keywordForecasts,\n keywordCount: keywords.length,\n forecastPeriod: startDate && endDate\n ? { startDate, endDate, days: periodDays }\n : { googleDefault: \"next Sunday through the following Saturday in the customer account time zone\" },\n settings: {\n matchType,\n negativeKeywordCount: negativeKeywords.length,\n geoTargetIds,\n languageIds,\n network,\n biddingStrategy,\n currencyCode: currencyCode ?? \"CUSTOMER_ACCOUNT_CURRENCY\",\n },\n warnings,\n limitations: [\n \"Google Ads v23 returns the planless forecast at campaign level only.\",\n \"keywordForecasts, when requested, simulate each keyword independently; they are not additive and can differ from the combined forecast because keywords compete and overlap.\",\n \"Forecasts are estimates influenced by the selected customer account, bids, budget, targeting, seasonality, and expected quality.\",\n ],\n nextActions: [\n \"Compare multiple bid/budget scenarios with the same targeting and period.\",\n \"Use historical metrics first to remove low-volume or irrelevant keywords before requesting breakdowns.\",\n ],\n debug: {\n requestCount,\n executionTimeMs: Date.now() - startedAt,\n timeBudgetMs: includeKeywordBreakdown\n ? FORECAST_WITH_BREAKDOWN_TIME_BUDGET_MS\n : null,\n },\n });\n } catch (error) {\n return formatMcpToolError(error);\n }\n }\n );\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { formatMcpToolError } from \"../../core/errors.js\";\nimport type { GoogleAdsClient } from \"./client.js\";\nimport type {\n GenerateAdGroupThemesRequest,\n GoogleAdsFieldCategory,\n SuggestGeoTargetConstantsRequest,\n} from \"./types.js\";\n\ntype ToolSuccessFormatter = (data: unknown) => {\n content: Array<{ type: \"text\"; text: string }>;\n};\n\nconst FIELD_COLUMNS = [\n \"name\",\n \"category\",\n \"data_type\",\n \"selectable\",\n \"filterable\",\n \"sortable\",\n \"is_repeated\",\n \"type_url\",\n \"enum_values\",\n \"selectable_with\",\n \"attribute_resources\",\n \"metrics\",\n \"segments\",\n].join(\", \");\n\nconst customerIdSchema = z.string().regex(\n /^\\d[\\d-]*\\d$|^\\d$/,\n \"Expected a numeric Google Ads customer ID, with or without dashes\"\n);\nconst numericIdSchema = z.string().regex(/^\\d+$/, \"Expected a numeric Google Ads ID\");\nconst fieldCategorySchema = z.enum([\n \"RESOURCE\",\n \"ATTRIBUTE\",\n \"SEGMENT\",\n \"METRIC\",\n]);\n\nfunction quoteFieldQuery(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\");\n}\n\nexport function validateGoogleAdsFieldsQuery(rawQuery: string): string {\n const query = rawQuery.replace(/^\\uFEFF/, \"\").trim().replace(/\\s+/g, \" \");\n const lexical = query.replace(/'(?:\\\\.|[^'\\\\])*'/g, \"''\");\n if (query.length < 6 || query.length > 50_000) {\n throw new Error(\"GoogleAdsField query length must be between 6 and 50,000 characters.\");\n }\n if (!/^SELECT\\b/i.test(lexical)) {\n throw new Error(\"GoogleAdsField discovery accepts only SELECT queries.\");\n }\n if ((lexical.match(/\\bSELECT\\b/gi) ?? []).length !== 1) {\n throw new Error(\"Exactly one GoogleAdsField SELECT query is allowed.\");\n }\n if (/;|--|\\/\\*|\\*\\/|\\0/.test(query)) {\n throw new Error(\"Semicolons, comments, and NUL bytes are not allowed.\");\n }\n if (/\\b(?:MUTATE|INSERT|UPDATE|DELETE|REMOVE|CREATE|ALTER|DROP|CALL|GRANT|REVOKE)\\b/i.test(lexical)) {\n throw new Error(\"Mutation or administrative keywords are not allowed.\");\n }\n return query;\n}\n\nexport function buildGoogleAdsFieldsQuery(input: {\n query?: string;\n nameContains?: string;\n category?: GoogleAdsFieldCategory;\n selectable?: boolean;\n filterable?: boolean;\n sortable?: boolean;\n}): string {\n if (input.query) return validateGoogleAdsFieldsQuery(input.query);\n\n const filters: string[] = [];\n if (input.nameContains) {\n filters.push(`name LIKE '%${quoteFieldQuery(input.nameContains)}%'`);\n }\n if (input.category) filters.push(`category = ${input.category}`);\n if (input.selectable !== undefined) filters.push(`selectable = ${input.selectable}`);\n if (input.filterable !== undefined) filters.push(`filterable = ${input.filterable}`);\n if (input.sortable !== undefined) filters.push(`sortable = ${input.sortable}`);\n const where = filters.length ? ` WHERE ${filters.join(\" AND \")}` : \"\";\n return `SELECT ${FIELD_COLUMNS}${where} ORDER BY name`;\n}\n\nfunction safeInteger(raw: string | undefined): number | null {\n if (!raw) return null;\n const value = Number(raw);\n return Number.isSafeInteger(value) ? value : null;\n}\n\nfunction idFromResourceName(resourceName: string | undefined): string | null {\n return resourceName?.split(\"/\").pop() ?? null;\n}\n\nexport function registerGoogleAdsDiscoveryTools(\n server: McpServer,\n client: GoogleAdsClient,\n ok: ToolSuccessFormatter\n): void {\n server.tool(\n \"google_ads_search_fields\",\n \"Search Google's live GoogleAdsField catalog. Discovers every queryable GAQL resource, attribute, segment, metric, enum value, and selectable-with compatibility relationship. Read-only and useful before a raw GAQL query.\",\n {\n query: z.string().trim().min(6).max(50_000).optional().describe(\"Optional raw GoogleAdsField SELECT query. When provided it replaces the structured filters.\"),\n nameContains: z.string().trim().min(1).max(250).optional().describe(\"Case-sensitive field-name substring, e.g. conversion or asset_group\"),\n category: fieldCategorySchema.optional(),\n selectable: z.boolean().optional(),\n filterable: z.boolean().optional(),\n sortable: z.boolean().optional(),\n pageSize: z.number().int().min(1).max(10_000).optional().default(500),\n pageToken: z.string().trim().min(1).max(10_000).optional(),\n },\n async ({ query, nameContains, category, selectable, filterable, sortable, pageSize, pageToken }) => {\n try {\n if (query && (nameContains || category || selectable !== undefined || filterable !== undefined || sortable !== undefined)) {\n throw new Error(\"Use either query or the structured field filters, not both.\");\n }\n const resolvedQuery = buildGoogleAdsFieldsQuery({\n query,\n nameContains,\n category,\n selectable,\n filterable,\n sortable,\n });\n const response = await client.searchGoogleAdsFields({\n query: resolvedQuery,\n pageSize,\n ...(pageToken ? { pageToken } : {}),\n });\n const fields = response.results ?? [];\n return ok({\n dataKind: \"google_ads_field_catalog\",\n query: resolvedQuery,\n fields,\n count: fields.length,\n totalResultsCount: safeInteger(response.totalResultsCount),\n totalResultsCountRaw: response.totalResultsCount ?? null,\n nextPageToken: response.nextPageToken ?? null,\n readOnly: true,\n warnings: [],\n limitations: [\"Field availability and selectable-with relationships are version-specific; this response reflects the configured Google Ads API version.\"],\n nextActions: [\"Use discovered field names with google_ads_run_gaql.\"],\n });\n } catch (error) {\n return formatMcpToolError(error);\n }\n }\n );\n\n server.tool(\n \"google_ads_suggest_geo_targets\",\n \"Resolve up to 25 location names or geo target IDs to Google Ads geoTargetConstants. Returns criterion IDs, canonical names, target types, status, parents, locale, and approximate reach. Read-only.\",\n {\n locationNames: z.array(z.string().trim().min(1).max(250)).min(1).max(25).optional(),\n geoTargetIds: z.array(numericIdSchema).min(1).max(25).optional(),\n locale: z.string().trim().regex(/^[A-Za-z]{2,3}(?:[-_][A-Za-z]{2,4})?$/, \"Expected a locale such as en, fr, or pt-BR\").optional().default(\"en\"),\n countryCode: z.string().trim().regex(/^[A-Za-z]{2}$/, \"Expected an ISO-3166 alpha-2 country code\").optional().transform((value) => value?.toUpperCase()),\n },\n async ({ locationNames, geoTargetIds, locale, countryCode }) => {\n try {\n if (Boolean(locationNames?.length) === Boolean(geoTargetIds?.length)) {\n throw new Error(\"Provide exactly one of locationNames or geoTargetIds.\");\n }\n const request: SuggestGeoTargetConstantsRequest = {\n locale,\n ...(countryCode ? { countryCode } : {}),\n ...(locationNames\n ? { locationNames: { names: [...new Set(locationNames)] } }\n : { geoTargets: { geoTargetConstants: [...new Set(geoTargetIds)].map((id) => `geoTargetConstants/${id}`) } }),\n };\n const response = await client.suggestGeoTargetConstants(request);\n const suggestions = (response.geoTargetConstantSuggestions ?? []).map((suggestion) => ({\n searchTerm: suggestion.searchTerm ?? null,\n locale: suggestion.locale ?? locale,\n reach: safeInteger(suggestion.reach),\n reachRaw: suggestion.reach ?? null,\n criterionId: idFromResourceName(suggestion.geoTargetConstant?.resourceName),\n geoTargetConstant: suggestion.geoTargetConstant ?? null,\n parents: suggestion.geoTargetConstantParents ?? [],\n }));\n return ok({\n dataKind: \"geo_target_suggestions\",\n suggestions,\n count: suggestions.length,\n request,\n readOnly: true,\n warnings: [],\n limitations: [\"Reach is approximate and rounded by Google.\"],\n nextActions: [\"Pass returned criterionId values as geoTargetIds to Keyword Planner tools or GAQL filters.\"],\n });\n } catch (error) {\n return formatMcpToolError(error);\n }\n }\n );\n\n server.tool(\n \"google_ads_generate_ad_group_themes\",\n \"Organize supplied keywords into existing Google Ads ad groups. Returns suggested ad group/campaign pairings, normalized keyword text, and suggested match type without creating or editing keywords. Read-only Keyword Planner RPC.\",\n {\n customerId: customerIdSchema.describe(\"Serving customer ID containing the existing ad groups\"),\n keywords: z.array(z.string().trim().min(1).max(80)).min(1).max(1_000),\n adGroupIds: z.array(numericIdSchema).min(1).max(200).describe(\"Existing ad group IDs in the same customer account\"),\n },\n async ({ customerId, keywords, adGroupIds }) => {\n try {\n const cleanCustomerId = customerId.replace(/-/g, \"\");\n const request: GenerateAdGroupThemesRequest = {\n keywords: [...new Set(keywords)],\n adGroups: [...new Set(adGroupIds)].map(\n (id) => `customers/${cleanCustomerId}/adGroups/${id}`\n ),\n };\n const response = await client.generateAdGroupThemes(cleanCustomerId, request);\n const suggestions = response.adGroupKeywordSuggestions ?? [];\n const unusableAdGroups = response.unusableAdGroups ?? [];\n return ok({\n dataKind: \"keyword_ad_group_themes\",\n suggestions,\n suggestionCount: suggestions.length,\n unusableAdGroups,\n unusableAdGroupCount: unusableAdGroups.length,\n requestedKeywordCount: request.keywords.length,\n requestedAdGroupCount: request.adGroups.length,\n readOnly: true,\n warnings: unusableAdGroups.length\n ? [\"Google could not use one or more supplied ad groups; inspect unusableAdGroups for their campaign context.\"]\n : [],\n limitations: [\"This RPC suggests organization into existing ad groups; it does not create themes, ad groups, or keywords.\"],\n nextActions: [\"Review match types and groupings before applying changes through the Google Ads UI or a separately authorized mutation workflow.\"],\n });\n } catch (error) {\n return formatMcpToolError(error);\n }\n }\n );\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { GOOGLE_ADS_METRIC_CATALOG } from \"./metric-catalog.js\";\nimport { GOOGLE_ADS_DIMENSION_CATALOG } from \"./dimension-catalog.js\";\nimport { GOOGLE_ADS_API_VERSION } from \"./types.js\";\n\nconst GOOGLE_ADS_TOOL_MANIFEST = {\n platform: \"google-ads\",\n apiVersion: GOOGLE_ADS_API_VERSION,\n safety: {\n scope: \"read-only\",\n mutatesData: false,\n secrets: \"Tools never return developer tokens, OAuth client secrets, refresh tokens, or access tokens.\",\n },\n tools: [\n { name: \"google_ads_health_check\", purpose: \"Verify credential presence, API reachability, accessible customers, login customer visibility, and actionable warnings.\" },\n { name: \"google_ads_list_accounts\", purpose: \"List Google Ads customer accounts accessible to the current credentials.\" },\n { name: \"google_ads_get_account_details\", purpose: \"Get customer-level account metadata.\" },\n { name: \"google_ads_get_account_hierarchy\", purpose: \"List accessible customers and MCC customer_client relationships when available.\" },\n { name: \"google_ads_get_campaigns\", purpose: \"List campaigns with status, budget, channel type, and bidding strategy.\" },\n { name: \"google_ads_get_adgroups\", purpose: \"List ad groups, optionally filtered by campaign/status.\" },\n { name: \"google_ads_get_budgets\", purpose: \"List campaign budgets with delivery, amount, status, and recommended budget fields.\" },\n { name: \"google_ads_get_bidding_strategies\", purpose: \"List portfolio bidding strategies and optional date-range metrics.\" },\n { name: \"google_ads_get_conversion_actions\", purpose: \"Audit conversion actions with type, category, primary/include flags, ownership, and last activity dates.\" },\n { name: \"google_ads_get_change_events\", purpose: \"Inspect recent account changes from change_event with a Google-enforced 30-day window and max 10000 rows.\" },\n { name: \"google_ads_get_recommendations\", purpose: \"List optimization recommendations with linked campaign/ad group/budget and impact when available.\" },\n { name: \"google_ads_get_search_terms\", purpose: \"Fetch search term performance from search_term_view or campaign_search_term_insight.\" },\n { name: \"google_ads_get_landing_pages\", purpose: \"Fetch landing_page_view performance and quality metrics.\" },\n { name: \"google_ads_get_pmax_assets\", purpose: \"List Performance Max asset group assets with structure and optional performance metrics.\" },\n { name: \"google_ads_get_simulations\", purpose: \"Read campaign, ad group, and portfolio bidding simulations for planning/forecast analysis.\" },\n { name: \"google_ads_get_paid_organic_search_terms\", purpose: \"Read paid/organic search term metrics with a paid-only search_term_view fallback.\" },\n { name: \"google_ads_get_shopping_products\", purpose: \"Read Merchant Center product catalog, eligibility, issues, and optional Shopping/PMax performance metrics.\" },\n { name: \"google_ads_get_shopping_performance\", purpose: \"Read Shopping performance keyed by merchant ID, item ID, title, brand, feed label, and custom labels.\" },\n { name: \"google_ads_get_pmax_placements\", purpose: \"Read Performance Max placement diagnostics with impression-only placement metrics.\" },\n { name: \"google_ads_get_pmax_asset_diagnostics\", purpose: \"Read Performance Max asset group diagnostics, asset coverage action items, and top combinations.\" },\n { name: \"google_ads_get_keyword_performance\", purpose: \"Fetch keyword-level performance and quality score.\" },\n { name: \"google_ads_generate_keyword_historical_metrics\", purpose: \"Return Keyword Planner search-volume history, competition, bids, close variants, and derived 3-month/YoY trends.\" },\n { name: \"google_ads_generate_keyword_ideas\", purpose: \"Discover Keyword Planner ideas from keyword, URL, combined, or site seeds with historical metrics and pagination.\" },\n { name: \"google_ads_generate_keyword_forecast_metrics\", purpose: \"Forecast campaign-level keyword impressions, clicks, cost, conversions, and optional independent per-keyword scenarios.\" },\n { name: \"google_ads_generate_ad_group_themes\", purpose: \"Organize keyword ideas into existing ad groups with suggested normalized text and match types.\" },\n { name: \"google_ads_suggest_geo_targets\", purpose: \"Resolve location names or criterion IDs to targetable Google Ads geo constants and approximate reach.\" },\n { name: \"google_ads_search_fields\", purpose: \"Search Google's live, version-specific catalog of GAQL resources, fields, enums, and compatibility relationships.\" },\n { name: \"google_ads_run_readonly_rpc\", purpose: \"Call an allowlisted non-GAQL read service for Audience Insights, Reach Planner, benchmarks, suggestions, identity, invoices, or payments metadata.\" },\n { name: \"google_ads_get_insights\", purpose: \"Generate validated performance GAQL using the metric and dimension catalogs.\" },\n { name: \"google_ads_validate_query\", purpose: \"Validate metric/dimension/resource compatibility before querying.\" },\n { name: \"google_ads_run_gaql\", purpose: \"Run raw read-only GAQL SELECT queries for advanced reporting.\" },\n ],\n resources: [\n \"google-ads://manifest\",\n \"google-ads://recipes\",\n \"google-ads://metrics\",\n \"google-ads://dimensions\",\n \"google-ads://compatibility\",\n ],\n};\n\nconst GOOGLE_ADS_RECIPES = [\n {\n name: \"Connection and access triage\",\n steps: [\n \"Call google_ads_health_check first.\",\n \"If manager accounts are present, call google_ads_get_account_hierarchy.\",\n \"Use returned customer IDs without dashes for all customerId parameters.\",\n ],\n },\n {\n name: \"Conversion tracking audit\",\n steps: [\n \"Call google_ads_get_conversion_actions for status/type/category/primary_for_goal coverage.\",\n \"Review metrics.conversion_last_conversion_date and metrics.conversion_last_received_request_date_time when present.\",\n \"Use google_ads_get_change_events for recent conversion action edits if suspicious changes are found.\",\n ],\n },\n {\n name: \"Budget and bidding review\",\n steps: [\n \"Call google_ads_get_budgets to inspect amount, delivery, status, and recommended budget fields.\",\n \"Call google_ads_get_bidding_strategies without dates for structure or with a date range for performance.\",\n \"Call google_ads_get_recommendations for budget and bidding recommendations before proposing changes.\",\n ],\n },\n {\n name: \"Search query mining\",\n steps: [\n \"Call google_ads_get_search_terms with reportType search_term_view for raw query performance.\",\n \"Use campaign_search_term_insight when Performance Max or privacy thresholds limit raw terms.\",\n \"Sort and filter results by impressions, cost, conversions, and search term status.\",\n ],\n },\n {\n name: \"Landing page and PMax asset audit\",\n steps: [\n \"Call google_ads_get_landing_pages for URL-level traffic, conversion, and landing-page quality metrics.\",\n \"Call google_ads_get_pmax_assets with a date range to include asset performance metrics.\",\n \"Call google_ads_get_pmax_asset_diagnostics for ad strength, asset coverage action items, primary status reasons, and top combinations.\",\n \"If warnings mention fallback queries, treat missing enriched fields as unavailable for that account/API combination.\",\n ],\n },\n {\n name: \"Keyword Planner research and forecast\",\n steps: [\n \"Call google_ads_generate_keyword_ideas to expand seed keywords or a landing-page/site URL.\",\n \"Call google_ads_suggest_geo_targets first when location criterion IDs are unknown.\",\n \"Call google_ads_generate_keyword_historical_metrics for monthly search history, competition, bids, close variants, and derived 3-month/YoY trends.\",\n \"Optionally call google_ads_generate_ad_group_themes to organize the shortlist into existing ad groups.\",\n \"Shortlist keywords, then call google_ads_generate_keyword_forecast_metrics with an explicit bid/budget, targeting, and future period.\",\n \"Treat historical searches as approximate demand and forecast impressions as campaign estimates; they are different data kinds.\",\n \"Request independent keyword breakdowns sparingly because Keyword Planner is limited to one request per second per customer ID.\",\n ],\n },\n {\n name: \"Discover the complete GAQL surface\",\n steps: [\n \"Call google_ads_search_fields with category RESOURCE to list queryable FROM resources.\",\n \"Search category METRIC, SEGMENT, or ATTRIBUTE by name and inspect selectableWith before composing a query.\",\n \"Run the final read-only SELECT with google_ads_run_gaql.\",\n ],\n },\n {\n name: \"Planning and forecast review\",\n steps: [\n \"Call google_ads_get_simulations at campaign, ad_group, or bidding_strategy level.\",\n \"Use typeFilter such as BUDGET, TARGET_CPA, TARGET_ROAS, or CPC_BID to narrow planning scenarios.\",\n \"If only metadata is returned, the account may not have generated simulation point lists for that entity.\",\n ],\n },\n {\n name: \"Paid and organic search coverage\",\n steps: [\n \"Call google_ads_get_paid_organic_search_terms for combined paid/organic query metrics.\",\n \"If the tool falls back to search_term_view_paid_only, organic fields are unavailable and the result should be treated as paid search terms only.\",\n \"Use serpType to focus ADS_AND_ORGANIC, ADS_ONLY, or ORGANIC_ONLY when paid_organic_search_term_view is available.\",\n ],\n },\n {\n name: \"Shopping and Merchant Center audit\",\n steps: [\n \"Call google_ads_get_shopping_products to inspect Merchant Center product eligibility, product issues, price, feed label, and item IDs.\",\n \"Call google_ads_get_shopping_performance to join spend/conversion metrics back to merchant ID and item ID.\",\n \"Use warnings from fallback queries to distinguish missing e-commerce/cart fields from true zero performance.\",\n ],\n },\n {\n name: \"PMax placement diagnostics\",\n steps: [\n \"Call google_ads_get_pmax_placements for placement type, display name, target URL, campaign, and impressions.\",\n \"Remember this Google Ads resource exposes impressions only; do not infer clicks, cost, or conversions from it.\",\n \"Use placementType and placementContains to focus websites, apps, or YouTube placements.\",\n ],\n },\n];\n\nexport function registerGoogleAdsResources(server: McpServer): void {\n server.resource(\"google-ads-manifest\", \"google-ads://manifest\", async () => ({\n contents: [{\n uri: \"google-ads://manifest\",\n mimeType: \"application/json\",\n text: JSON.stringify(GOOGLE_ADS_TOOL_MANIFEST, null, 2),\n }],\n }));\n\n server.resource(\"google-ads-recipes\", \"google-ads://recipes\", async () => ({\n contents: [{\n uri: \"google-ads://recipes\",\n mimeType: \"application/json\",\n text: JSON.stringify(GOOGLE_ADS_RECIPES, null, 2),\n }],\n }));\n\n server.resource(\"google-ads-metrics\", \"google-ads://metrics\", async () => ({\n contents: [{\n uri: \"google-ads://metrics\",\n mimeType: \"application/json\",\n text: JSON.stringify(GOOGLE_ADS_METRIC_CATALOG.map(m => ({\n key: m.key, name: m.name, description: m.description,\n category: m.category, type: m.type, format: m.format,\n apiField: m.apiField,\n })), null, 2),\n }],\n }));\n\n server.resource(\"google-ads-dimensions\", \"google-ads://dimensions\", async () => ({\n contents: [{\n uri: \"google-ads://dimensions\",\n mimeType: \"application/json\",\n text: JSON.stringify(GOOGLE_ADS_DIMENSION_CATALOG.map(d => ({\n key: d.key, name: d.name, description: d.description,\n category: d.category, apiField: d.apiField,\n isSegment: d.isSegment, isResourceAttribute: d.isResourceAttribute,\n })), null, 2),\n }],\n }));\n\n server.resource(\"google-ads-compatibility\", \"google-ads://compatibility\", async () => ({\n contents: [{\n uri: \"google-ads://compatibility\",\n mimeType: \"application/json\",\n text: JSON.stringify({\n description: \"Google Ads uses GAQL (SQL-like). The FROM clause determines the resource type. Some metrics are restricted to specific resources, and some segments are incompatible with certain metrics (especially impression_share).\",\n resourceTypes: [\"campaign\", \"ad_group\", \"ad_group_ad\", \"keyword_view\", \"search_term_view\", \"paid_organic_search_term_view\", \"shopping_performance_view\", \"shopping_product\", \"asset_group\", \"asset_group_asset\", \"asset_group_top_combination_view\", \"performance_max_placement_view\", \"campaign_simulation\", \"ad_group_simulation\", \"bidding_strategy_simulation\"],\n queryFormat: \"SELECT ... FROM resource WHERE ... ORDER BY ... LIMIT N\",\n }, null, 2),\n }],\n }));\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n/**\n * Write tools for the Google Ads API.\n *\n * These are registered only when `GOOGLE_ADS_ENABLE_WRITES` is set. They reuse\n * the read client, so they share its cached OAuth token rather than refreshing\n * on every call.\n *\n * Part of google-ads-mcp-server: https://github.com/getmcpads-com/google-ads-mcp-server\n * Managed, multi-platform version: https://www.getmcpads.com\n */\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { GoogleAdsConfig } from \"../../config.js\";\nimport { GoogleAdsClient } from \"./client.js\";\n\nfunction ok(data: unknown) {\n return { content: [{ type: \"text\" as const, text: JSON.stringify(data, null, 2) }] };\n}\n\nfunction ko(message: string) {\n return { isError: true, content: [{ type: \"text\" as const, text: message }] };\n}\n\n/**\n * Every write is a preview until `confirm` is true.\n *\n * An assistant composes these calls, and it can pick the wrong customer, the\n * wrong campaign, or the wrong order of magnitude on a budget. A mandatory\n * preview makes the mistake visible before it costs money, and gives a human\n * the stopping point the protocol does not guarantee on its own.\n */\nfunction preview(action: string, details: Record<string, unknown>) {\n return ok({\n applied: false,\n action,\n change: details,\n message:\n \"Preview only, nothing was changed. Repeat the same call with confirm: true \" +\n \"to apply this change to the live account.\",\n });\n}\n\nconst confirmSchema = z\n .boolean()\n .optional()\n .describe(\"Set to true to actually apply the change. Without it, the tool only previews.\");\n\nconst loginCustomerIdSchema = z\n .string()\n .optional()\n .describe(\"Manager account ID, when the customer sits under an MCC. Omit otherwise.\");\n\n/** Google Ads holds money in micros: 1.20 in the account currency is 1200000. */\nfunction toMicros(amount: number): number {\n if (!Number.isFinite(amount) || amount <= 0) {\n throw new Error(`Expected a positive amount, received \"${amount}\".`);\n }\n return Math.round(amount * 1_000_000);\n}\n\nconst stripDashes = (id: unknown) => String(id).replace(/-/g, \"\");\n\nexport function registerGoogleAdsWrites(server: McpServer, config: GoogleAdsConfig): void {\n const client = new GoogleAdsClient(config);\n\n const resourceName = (cid: string, collection: string, id: unknown) =>\n `customers/${cid}/${collection}/${id}`;\n\n // ── Status: pause, enable, remove ─────────────────────────────────\n const statusTool = (\n name: string,\n label: \"campaign\" | \"ad group\",\n param: string,\n collection: string,\n ) =>\n server.tool(\n name,\n `Pause, re-enable or remove a Google Ads ${label}. Previews by default: without ` +\n `confirm: true, the tool describes the change without applying it.`,\n {\n customerId: z.string().describe(\"Google Ads customer ID, with or without dashes.\"),\n [param]: z.string().describe(`${label} ID.`),\n status: z.enum([\"PAUSED\", \"ENABLED\", \"REMOVED\"]).describe(\"New status.\"),\n loginCustomerId: loginCustomerIdSchema,\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const cid = stripDashes(a.customerId);\n const id = String(a[param]);\n if (!a.confirm) {\n return preview(name, { customer: cid, target: id, newStatus: a.status });\n }\n const result = await client.mutate(cid, collection, [\n { update: { resourceName: resourceName(cid, collection, id), status: a.status }, updateMask: \"status\" },\n ], a.loginCustomerId as string | undefined);\n return ok({ applied: true, action: name, result });\n },\n );\n\n statusTool(\"google_ads_update_campaign_status\", \"campaign\", \"campaignId\", \"campaigns\");\n statusTool(\"google_ads_update_adgroup_status\", \"ad group\", \"adGroupId\", \"adGroups\");\n\n // ── Rename ────────────────────────────────────────────────────────\n server.tool(\n \"google_ads_rename_campaign\",\n \"Rename a Google Ads campaign. The name is the only thing that changes: delivery, budget \" +\n \"and targeting are untouched. Previews by default.\",\n {\n customerId: z.string().describe(\"Google Ads customer ID, with or without dashes.\"),\n campaignId: z.string().describe(\"Campaign ID.\"),\n name: z.string().min(1).max(255).describe(\"New campaign name.\"),\n loginCustomerId: loginCustomerIdSchema,\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const cid = stripDashes(a.customerId);\n if (!a.confirm) {\n return preview(\"google_ads_rename_campaign\", { customer: cid, campaign: a.campaignId, newName: a.name });\n }\n const result = await client.mutate(cid, \"campaigns\", [\n { update: { resourceName: resourceName(cid, \"campaigns\", a.campaignId), name: a.name }, updateMask: \"name\" },\n ], a.loginCustomerId as string | undefined);\n return ok({ applied: true, action: \"google_ads_rename_campaign\", result });\n },\n );\n\n // ── Create campaign (always paused, budget first) ─────────────────\n server.tool(\n \"google_ads_create_campaign\",\n \"Create a Google Ads campaign. It is always created PAUSED and there is no option to \" +\n \"create it active: someone has to look at it before it spends. Creates the campaign \" +\n \"budget too. Previews by default.\",\n {\n customerId: z.string().describe(\"Google Ads customer ID, with or without dashes.\"),\n name: z.string().min(1).max(255).describe(\"Campaign name.\"),\n channelType: z\n .enum([\"SEARCH\", \"DISPLAY\", \"SHOPPING\", \"VIDEO\", \"PERFORMANCE_MAX\"])\n .describe(\"Advertising channel.\"),\n dailyBudget: z.number().positive().describe(\"Daily budget in the account currency.\"),\n budgetName: z\n .string()\n .min(1)\n .max(255)\n .describe(\"Name for the campaign budget. Google requires it to be unique on the account.\"),\n loginCustomerId: loginCustomerIdSchema,\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const cid = stripDashes(a.customerId);\n let micros: number;\n try {\n micros = toMicros(Number(a.dailyBudget));\n } catch (error) {\n return ko(error instanceof Error ? error.message : String(error));\n }\n if (!a.confirm) {\n return preview(\"google_ads_create_campaign\", {\n customer: cid, name: a.name, channelType: a.channelType,\n dailyBudget: a.dailyBudget, inMicros: micros,\n budgetName: a.budgetName, status: \"PAUSED\",\n });\n }\n\n const login = a.loginCustomerId as string | undefined;\n // The budget has to exist before the campaign: Google refuses a campaign\n // without one, and the budget name has to be unique on the account.\n const budget = (await client.mutate(cid, \"campaignBudgets\", [\n {\n create: {\n name: a.budgetName,\n amountMicros: String(micros),\n deliveryMethod: \"STANDARD\",\n },\n },\n ], login)) as { results?: { resourceName: string }[] };\n\n const budgetResource = budget.results?.[0]?.resourceName;\n if (!budgetResource) {\n return ko(\"Google Ads did not return a budget resource name, so the campaign was not created.\");\n }\n\n const campaign = await client.mutate(cid, \"campaigns\", [\n {\n create: {\n name: a.name,\n status: \"PAUSED\",\n advertisingChannelType: a.channelType,\n campaignBudget: budgetResource,\n manualCpc: {},\n },\n },\n ], login);\n\n return ok({\n applied: true,\n action: \"google_ads_create_campaign\",\n status: \"PAUSED\",\n budget: budgetResource,\n result: campaign,\n });\n },\n );\n\n // ── Budget and bid ────────────────────────────────────────────────\n server.tool(\n \"google_ads_update_campaign_budget\",\n \"Change the daily budget of a Google Ads campaign. The amount is in the account currency \" +\n \"(12.50 for 12.50 EUR). Previews by default.\",\n {\n customerId: z.string().describe(\"Google Ads customer ID, with or without dashes.\"),\n budgetId: z.string().describe(\"Campaign budget ID (campaign_budget.id).\"),\n dailyAmount: z.number().positive().describe(\"New daily budget, in the account currency.\"),\n loginCustomerId: loginCustomerIdSchema,\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const cid = stripDashes(a.customerId);\n let micros: number;\n try {\n micros = toMicros(Number(a.dailyAmount));\n } catch (error) {\n return ko(error instanceof Error ? error.message : String(error));\n }\n if (!a.confirm) {\n return preview(\"google_ads_update_campaign_budget\", {\n customer: cid, budget: a.budgetId, newDailyBudget: a.dailyAmount, inMicros: micros,\n });\n }\n const result = await client.mutate(cid, \"campaignBudgets\", [\n {\n update: {\n resourceName: resourceName(cid, \"campaignBudgets\", a.budgetId),\n amountMicros: String(micros),\n },\n updateMask: \"amount_micros\",\n },\n ], a.loginCustomerId as string | undefined);\n return ok({ applied: true, action: \"google_ads_update_campaign_budget\", result });\n },\n );\n\n server.tool(\n \"google_ads_update_adgroup_bid\",\n \"Change the default CPC bid of a Google Ads ad group. The amount is in the account \" +\n \"currency (1.20 for 1.20 EUR). Has no effect on a campaign using an automated bidding \" +\n \"strategy. Previews by default.\",\n {\n customerId: z.string().describe(\"Google Ads customer ID, with or without dashes.\"),\n adGroupId: z.string().describe(\"Ad group ID.\"),\n cpcBid: z.number().positive().describe(\"New default CPC bid, in the account currency.\"),\n loginCustomerId: loginCustomerIdSchema,\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const cid = stripDashes(a.customerId);\n let micros: number;\n try {\n micros = toMicros(Number(a.cpcBid));\n } catch (error) {\n return ko(error instanceof Error ? error.message : String(error));\n }\n if (!a.confirm) {\n return preview(\"google_ads_update_adgroup_bid\", {\n customer: cid, adGroup: a.adGroupId, newCpcBid: a.cpcBid, inMicros: micros,\n });\n }\n const result = await client.mutate(cid, \"adGroups\", [\n {\n update: {\n resourceName: resourceName(cid, \"adGroups\", a.adGroupId),\n cpcBidMicros: String(micros),\n },\n updateMask: \"cpc_bid_micros\",\n },\n ], a.loginCustomerId as string | undefined);\n return ok({ applied: true, action: \"google_ads_update_adgroup_bid\", result });\n },\n );\n\n // ── Schedule ──────────────────────────────────────────────────────\n server.tool(\n \"google_ads_update_campaign_schedule\",\n \"Change the start or end date of a Google Ads campaign. Dates are YYYY-MM-DD in the \" +\n \"account time zone. Previews by default.\",\n {\n customerId: z.string().describe(\"Google Ads customer ID, with or without dashes.\"),\n campaignId: z.string().describe(\"Campaign ID.\"),\n startDate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional()\n .describe(\"Start date, YYYY-MM-DD, in the account time zone.\"),\n endDate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/).optional()\n .describe(\"End date, YYYY-MM-DD. Use 2037-12-30 to mean no end date, which is what Google stores.\"),\n loginCustomerId: loginCustomerIdSchema,\n confirm: confirmSchema,\n },\n async (a: Record<string, unknown>) => {\n const { startDate, endDate } = a as Record<string, string | undefined>;\n if (!startDate && !endDate) return ko(\"Provide startDate, endDate, or both.\");\n if (startDate && endDate && startDate > endDate) {\n return ko(`startDate ${startDate} is after endDate ${endDate}.`);\n }\n const cid = stripDashes(a.customerId);\n const update: Record<string, unknown> = {\n resourceName: resourceName(cid, \"campaigns\", a.campaignId),\n };\n const mask: string[] = [];\n if (startDate) { update.startDate = startDate; mask.push(\"start_date\"); }\n if (endDate) { update.endDate = endDate; mask.push(\"end_date\"); }\n\n if (!a.confirm) {\n return preview(\"google_ads_update_campaign_schedule\", {\n customer: cid, campaign: a.campaignId, startDate, endDate,\n });\n }\n const result = await client.mutate(cid, \"campaigns\", [\n { update, updateMask: mask.join(\",\") },\n ], a.loginCustomerId as string | undefined);\n return ok({ applied: true, action: \"google_ads_update_campaign_schedule\", result });\n },\n );\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { GoogleAdsConfig } from \"../../config.js\";\nimport { registerGoogleAdsTools } from \"./tools.js\";\nimport { registerGoogleAdsResources } from \"./resources.js\";\nimport { registerGoogleAdsWrites } from \"./writes.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport function registerGoogleAds(server: McpServer, config: GoogleAdsConfig): void {\n registerGoogleAdsTools(server, config);\n registerGoogleAdsResources(server);\n logger.info(\"google-ads\", \"Registered 31 read tools and 5 resources\");\n\n if (config.enableWrites) {\n registerGoogleAdsWrites(server, config);\n logger.info(\"google-ads\", \"Registered 7 write tools (every one previews before it applies)\");\n }\n}\n","/**\n * google-ads-mcp-server: an open-source MCP server for the Google Ads API.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\nimport { logger } from \"./core/logger.js\";\n\nconst configSchema = z.object({\n developerToken: z.string().min(1, \"GOOGLE_ADS_DEVELOPER_TOKEN is required\"),\n clientId: z.string().min(1, \"GOOGLE_ADS_CLIENT_ID is required\"),\n clientSecret: z.string().min(1, \"GOOGLE_ADS_CLIENT_SECRET is required\"),\n refreshToken: z.string().min(1, \"GOOGLE_ADS_REFRESH_TOKEN is required\"),\n loginCustomerId: z.string().optional(),\n /** Write tools are registered only when this is true. */\n enableWrites: z.boolean().optional(),\n logLevel: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n});\n\nexport type GoogleAdsConfig = z.infer<typeof configSchema>;\n\nexport function loadConfig(): GoogleAdsConfig {\n const raw = {\n developerToken: process.env[\"GOOGLE_ADS_DEVELOPER_TOKEN\"] ?? \"\",\n clientId: process.env[\"GOOGLE_ADS_CLIENT_ID\"] ?? \"\",\n clientSecret: process.env[\"GOOGLE_ADS_CLIENT_SECRET\"] ?? \"\",\n refreshToken: process.env[\"GOOGLE_ADS_REFRESH_TOKEN\"] ?? \"\",\n loginCustomerId: process.env[\"GOOGLE_ADS_LOGIN_CUSTOMER_ID\"] || undefined,\n enableWrites: isTruthy(process.env[\"GOOGLE_ADS_ENABLE_WRITES\"]),\n logLevel: process.env[\"LOG_LEVEL\"] ?? \"info\",\n };\n\n const result = configSchema.safeParse(raw);\n if (!result.success) {\n const missing = result.error.issues.map(i => i.message).join(\", \");\n logger.error(\"config\", `Missing credentials: ${missing}`);\n throw new Error(`Missing Google Ads credentials: ${missing}`);\n }\n\n return result.data;\n}\n\n/** Accepts the spellings people actually type in an MCP client config. */\nfunction isTruthy(value: string | undefined): boolean {\n if (!value) return false;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(value.trim().toLowerCase());\n}\n"],"mappings":"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","names":["z","IMPRESSION_SHARE_METRICS","ok","z","z","ok","z","customerIdSchema","z","numericIdSchema","ok","customerIdSchema","z","isoDateSchema","numericIdSchema","z","ok","z","loginCustomerIdSchema","z","z"]}